agora inbox for [email protected]help / color / mirror / Atom feed
[PATCH v2 1/1] Lower default value of autovacuum_worker_slots in initdb as needed. 265+ messages / 2 participants [nested] [flat]
* [PATCH v2 1/1] Lower default value of autovacuum_worker_slots in initdb as needed. @ 2025-01-06 23:14 Nathan Bossart <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Nathan Bossart @ 2025-01-06 23:14 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 5 +-- src/backend/utils/misc/guc_tables.c | 2 +- src/backend/utils/misc/postgresql.conf.sample | 2 +- src/bin/initdb/initdb.c | 34 +++++++++++++++---- 4 files changed, 33 insertions(+), 10 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index 740ff5d5044..a47c4dbfcb2 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -8639,8 +8639,9 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv; <listitem> <para> Specifies the number of backend slots to reserve for autovacuum worker - processes. The default is 16. This parameter can only be set at server - start. + processes. The default is typically 12 slots, but might be less if + your kernel settings will not support it (as determined during initdb). + This parameter can only be set at server start. </para> <para> When changing this value, consider also adjusting diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index c9d8cd796a8..8781495a622 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -3472,7 +3472,7 @@ struct config_int ConfigureNamesInt[] = NULL }, &autovacuum_worker_slots, - 16, 1, MAX_BACKENDS, + 12, 1, MAX_BACKENDS, NULL, NULL, NULL }, { diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample index b2bc43383db..3405ca0e726 100644 --- a/src/backend/utils/misc/postgresql.conf.sample +++ b/src/backend/utils/misc/postgresql.conf.sample @@ -661,7 +661,7 @@ #autovacuum = on # Enable autovacuum subprocess? 'on' # requires track_counts to also be on. -autovacuum_worker_slots = 16 # autovacuum worker slots to allocate +autovacuum_worker_slots = 12 # autovacuum worker slots to allocate # (change requires restart) #autovacuum_max_workers = 3 # max number of autovacuum subprocesses #autovacuum_naptime = 1min # time between autovacuum runs diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c index 4e4b7ede190..b5023484771 100644 --- a/src/bin/initdb/initdb.c +++ b/src/bin/initdb/initdb.c @@ -196,6 +196,7 @@ static char *pgdata_native; /* defaults */ static int n_connections = 10; +static int n_av_slots = 12; static int n_buffers = 50; static const char *dynamic_shared_memory_type = NULL; static const char *default_timezone = NULL; @@ -273,7 +274,8 @@ static void check_input(char *path); static void write_version_file(const char *extrapath); static void set_null_conf(void); static void test_config_settings(void); -static bool test_specific_config_settings(int test_conns, int test_buffs); +static bool test_specific_config_settings(int test_conns, int test_av_slots, + int test_buffs); static void setup_config(void); static void bootstrap_template1(void); static void setup_auth(FILE *cmdfd); @@ -1118,6 +1120,12 @@ test_config_settings(void) */ #define MIN_BUFS_FOR_CONNS(nconns) ((nconns) * 10) + /* + * This macro defines the default value of autovacuum_worker_slots we want + * for a given max_connections value. + */ +#define AV_SLOTS_FOR_CONNS(nconns) ((nconns) / 8) + static const int trial_conns[] = { 100, 50, 40, 30, 25 }; @@ -1145,7 +1153,8 @@ test_config_settings(void) /* * Probe for max_connections before shared_buffers, since it is subject to - * more constraints than shared_buffers. + * more constraints than shared_buffers. We also choose the default + * autovacuum_worker_slots here. */ printf(_("selecting default \"max_connections\" ... ")); fflush(stdout); @@ -1154,8 +1163,9 @@ test_config_settings(void) { test_conns = trial_conns[i]; test_buffs = MIN_BUFS_FOR_CONNS(test_conns); + n_av_slots = AV_SLOTS_FOR_CONNS(test_conns); - if (test_specific_config_settings(test_conns, test_buffs)) + if (test_specific_config_settings(test_conns, n_av_slots, test_buffs)) { ok_buffers = test_buffs; break; @@ -1167,6 +1177,13 @@ test_config_settings(void) printf("%d\n", n_connections); + /* + * We chose the default for autovacuum_worker_slots during the + * max_connections tests above, but we print a progress message anyway. + */ + printf(_("selecting default \"autovacuum_worker_slots\" ... %d\n"), + n_av_slots); + printf(_("selecting default \"shared_buffers\" ... ")); fflush(stdout); @@ -1180,7 +1197,7 @@ test_config_settings(void) break; } - if (test_specific_config_settings(n_connections, test_buffs)) + if (test_specific_config_settings(n_connections, n_av_slots, test_buffs)) break; } n_buffers = test_buffs; @@ -1200,7 +1217,7 @@ test_config_settings(void) * Test a specific combination of configuration settings. */ static bool -test_specific_config_settings(int test_conns, int test_buffs) +test_specific_config_settings(int test_conns, int test_av_slots, int test_buffs) { PQExpBufferData cmd; _stringlist *gnames, @@ -1213,10 +1230,11 @@ test_specific_config_settings(int test_conns, int test_buffs) printfPQExpBuffer(&cmd, "\"%s\" --check %s %s " "-c max_connections=%d " + "-c autovacuum_worker_slots=%d " "-c shared_buffers=%d " "-c dynamic_shared_memory_type=%s", backend_exec, boot_options, extra_options, - test_conns, test_buffs, + test_conns, test_av_slots, test_buffs, dynamic_shared_memory_type); /* Add any user-given setting overrides */ @@ -1280,6 +1298,10 @@ setup_config(void) conflines = replace_guc_value(conflines, "max_connections", repltok, false); + snprintf(repltok, sizeof(repltok), "%d", n_av_slots); + conflines = replace_guc_value(conflines, "autovacuum_worker_slots", + repltok, false); + if ((n_buffers * (BLCKSZ / 1024)) % 1024 == 0) snprintf(repltok, sizeof(repltok), "%dMB", (n_buffers * (BLCKSZ / 1024)) / 1024); -- 2.39.5 (Apple Git-154) --Pf5SPdkAJilNBD4g-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 265+ messages in thread
end of thread, other threads:[~2025-07-27 15:48 UTC | newest] Thread overview: 265+ messages (download: mbox mbox.gz follow: Atom feed) -- links below jump to the message on this page -- 2025-01-06 23:14 [PATCH v2 1/1] Lower default value of autovacuum_worker_slots in initdb as needed. Nathan Bossart <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[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