agora inbox for [email protected]help / color / mirror / Atom feed
Major Problem, need help! Can't run our website! 274+ messages / 9 participants [nested] [flat]
* Major Problem, need help! Can't run our website! @ 2005-11-15 03:19 ITS ONT Alcazar, Jose Aguedo C <[email protected]> 0 siblings, 1 reply; 274+ messages in thread From: ITS ONT Alcazar, Jose Aguedo C @ 2005-11-15 03:19 UTC (permalink / raw) To: [email protected] Anyone! Before anything else, I have no background in PostgreSQL. But I have a little knowledge in Linux. We used postgreSQL to run one of our website. It runs in Redhat Linux 7.3. Our System Administrator, who used to maintain this server, had resigned and didn't have a proper documentation on how to maintain this server. Right now, our NEW System Administrator is clearing some logs in /var/lib/pgsql/data/pg_xlog in able to free some space in the /var file system. It used to work before, but now, its not working anymore. Information below is the message we are encountering when we are trying to connect to the website. Please, ANYONE, help us! Warning: Unable to connect to PostgreSQL server: could not connect to server: Connection refused Is the server running on host localhost and accepting TCP/IP connections on port 5432? in /var/lib/phplib-7.2d/php/db_pgsql.inc on line 48 Database error: Link-ID == false, pconnect failed ODBC Error: 0 () Session halted. Thanks! --AGUEDO ^ permalink raw reply [nested|flat] 274+ messages in thread
* Re: Major Problem, need help! Can't run our website! @ 2005-11-15 03:48 Tim Allen <[email protected]> parent: ITS ONT Alcazar, Jose Aguedo C <[email protected]> 0 siblings, 3 replies; 274+ messages in thread From: Tim Allen @ 2005-11-15 03:48 UTC (permalink / raw) To: pgsql-hackers; +Cc: [email protected] ITS ONT Alcazar, Jose Aguedo C wrote: > Anyone! > > Before anything else, I have no background in PostgreSQL. But I have a > little knowledge in Linux. We used postgreSQL to run one of our website. It > runs in Redhat Linux 7.3. Our System Administrator, who used to maintain > this server, had resigned and didn't have a proper documentation on how to > maintain this server. Right now, our NEW System Administrator is clearing > some logs in /var/lib/pgsql/data/pg_xlog in able to free some space in the > /var file system. It used to work before, but now, its not working anymore. > Information below is the message we are encountering when we are trying to > connect to the website. Please, ANYONE, help us! We've seen reports of people firing this particular foot-gun before, haven't we? Would it make sense to rename pg_xlog to something that doesn't sound like it's "just" full of log files? Eg pg_wal - something where the half-educated will have no idea what it is, and therefore not think they know what they can do with it. Tim -- ----------------------------------------------- Tim Allen [email protected] Proximity Pty Ltd http://www.proximity.com.au/ ^ permalink raw reply [nested|flat] 274+ messages in thread
* Re: [HACKERS] Major Problem, need help! Can't run our website! @ 2005-11-15 04:02 Tom Lane <[email protected]> parent: Tim Allen <[email protected]> 2 siblings, 1 reply; 274+ messages in thread From: Tom Lane @ 2005-11-15 04:02 UTC (permalink / raw) To: Tim Allen <[email protected]>; +Cc: pgsql-hackers; [email protected] Tim Allen <[email protected]> writes: > We've seen reports of people firing this particular foot-gun before, > haven't we? Would it make sense to rename pg_xlog to something that > doesn't sound like it's "just" full of log files? Eg pg_wal - something > where the half-educated will have no idea what it is, and therefore not > think they know what they can do with it. There's something in what you say. We'd have to rename pg_clog as well, since that's even more critical than pg_xlog ... regards, tom lane ^ permalink raw reply [nested|flat] 274+ messages in thread
* Re: [HACKERS] Major Problem, need help! Can't run our website! @ 2005-11-15 04:05 Christopher Kings-Lynne <[email protected]> parent: Tim Allen <[email protected]> 2 siblings, 1 reply; 274+ messages in thread From: Christopher Kings-Lynne @ 2005-11-15 04:05 UTC (permalink / raw) To: Tim Allen <[email protected]>; +Cc: pgsql-hackers; [email protected] > We've seen reports of people firing this particular foot-gun before, > haven't we? Would it make sense to rename pg_xlog to something that > doesn't sound like it's "just" full of log files? Eg pg_wal - something > where the half-educated will have no idea what it is, and therefore not > think they know what they can do with it. Would it be wise or insane for us to to mention in the startup error a HINT that if you've removed such files, only hope is full restore from backup or pg_resetxlog with data loss? Chris ^ permalink raw reply [nested|flat] 274+ messages in thread
* Re: [HACKERS] Major Problem, need help! Can't run our website! @ 2005-11-15 04:17 Tom Lane <[email protected]> parent: Christopher Kings-Lynne <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Tom Lane @ 2005-11-15 04:17 UTC (permalink / raw) To: Christopher Kings-Lynne <[email protected]>; +Cc: Tim Allen <[email protected]>; pgsql-hackers; [email protected] Christopher Kings-Lynne <[email protected]> writes: > Would it be wise or insane for us to to mention in the startup error a > HINT that if you've removed such files, only hope is full restore from > backup or pg_resetxlog with data loss? Not sure that we should have a HINT recommending a worst-case-scenario course of action as the first resort. We'll have people blowing away their data for what might be relatively fixable problems (eg, bogus permissions on the pg_xlog directory, which I think was an issue that just came up a day or two ago ...) (We're all really jumping to conclusions here anyway. The guy may have been foolish to remove xlog files, but that doesn't explain why his postmaster isn't running. There's some facts missing in that report.) regards, tom lane ^ permalink raw reply [nested|flat] 274+ messages in thread
* Re: [HACKERS] Major Problem, need help! Can't run our @ 2005-11-15 04:32 Rod Taylor <[email protected]> parent: Tom Lane <[email protected]> 0 siblings, 1 reply; 274+ messages in thread From: Rod Taylor @ 2005-11-15 04:32 UTC (permalink / raw) To: Tom Lane <[email protected]>; +Cc: Tim Allen <[email protected]>; pgsql-hackers; [email protected] On Mon, 2005-11-14 at 23:02 -0500, Tom Lane wrote: > Tim Allen <[email protected]> writes: > > We've seen reports of people firing this particular foot-gun before, > > haven't we? Would it make sense to rename pg_xlog to something that > > doesn't sound like it's "just" full of log files? Eg pg_wal - something > > where the half-educated will have no idea what it is, and therefore not > > think they know what they can do with it. > > There's something in what you say. We'd have to rename pg_clog as well, > since that's even more critical than pg_xlog ... Rename them to pg_donttouchthis and pg_youneedthis. -- ^ permalink raw reply [nested|flat] 274+ messages in thread
* Re: [HACKERS] Major Problem, need help! Can't run our @ 2005-11-15 04:48 Tom Lane <[email protected]> parent: Rod Taylor <[email protected]> 0 siblings, 1 reply; 274+ messages in thread From: Tom Lane @ 2005-11-15 04:48 UTC (permalink / raw) To: Rod Taylor <[email protected]>; +Cc: Tim Allen <[email protected]>; pgsql-hackers; [email protected] Rod Taylor <[email protected]> writes: > On Mon, 2005-11-14 at 23:02 -0500, Tom Lane wrote: >> There's something in what you say. We'd have to rename pg_clog as well, >> since that's even more critical than pg_xlog ... > Rename them to pg_donttouchthis and pg_youneedthis. :-) On a more serious level: Tim's suggestion of "pg_wal" for pg_xlog sounds fine to me. How about "pg_trans" for pg_clog, by analogy to the existing pg_subtrans? Nothing else in the standard layout looks like it's got a name that a newbie would think means discardable data. regards, tom lane ^ permalink raw reply [nested|flat] 274+ messages in thread
* Re: [HACKERS] Major Problem, need help! Can't run our @ 2005-11-15 06:28 Jonah H. Harris <[email protected]> parent: Tom Lane <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Jonah H. Harris @ 2005-11-15 06:28 UTC (permalink / raw) To: Tom Lane <[email protected]>; +Cc: Rod Taylor <[email protected]>; Tim Allen <[email protected]>; pgsql-hackers; [email protected] I agree. (sorry again Tom... dang GMAIL should default reply to all.... grrrr!) On 11/14/05, Tom Lane <[email protected]> wrote: > > Rod Taylor <[email protected]> writes: > > On Mon, 2005-11-14 at 23:02 -0500, Tom Lane wrote: > >> There's something in what you say. We'd have to rename pg_clog as well, > >> since that's even more critical than pg_xlog ... > > > Rename them to pg_donttouchthis and pg_youneedthis. > > :-) > > On a more serious level: Tim's suggestion of "pg_wal" for pg_xlog sounds > fine to me. How about "pg_trans" for pg_clog, by analogy to the > existing pg_subtrans? Nothing else in the standard layout looks like > it's got a name that a newbie would think means discardable data. > > regards, tom lane > > ---------------------------(end of broadcast)--------------------------- > TIP 3: Have you checked our extensive FAQ? > > http://www.postgresql.org/docs/faq > ^ permalink raw reply [nested|flat] 274+ messages in thread
* Re: [ADMIN] Major Problem, need help! Can't run our website! @ 2005-11-15 23:27 Trent Shipley <[email protected]> parent: Tim Allen <[email protected]> 2 siblings, 1 reply; 274+ messages in thread From: Trent Shipley @ 2005-11-15 23:27 UTC (permalink / raw) To: pgsql-hackers On Monday 2005-11-14 20:48, Tim Allen wrote: <snip> OOPS deleted pg_xlog because surely it was only a log file. </snip> > > We've seen reports of people firing this particular foot-gun before, > haven't we? Would it make sense to rename pg_xlog to something that > doesn't sound like it's "just" full of log files? Eg pg_wal - something > where the half-educated will have no idea what it is, and therefore not > think they know what they can do with it. > > Tim Renaming the file sounds like an excellent design decision since the current name is a proven "human factor" bug. ^ permalink raw reply [nested|flat] 274+ messages in thread
* Re: [ADMIN] Major Problem, need help! Can't run our website! @ 2005-11-16 01:27 Joshua D. Drake <[email protected]> parent: Trent Shipley <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Joshua D. Drake @ 2005-11-16 01:27 UTC (permalink / raw) To: [email protected]; +Cc: pgsql-hackers > > Renaming the file sounds like an excellent design decision since the current > name is a proven "human factor" bug. > I am sorry, but as soon as you look at the files it is obvious that they are not "just" log files. If someone is going to delete the xlog they are going to do it no matter what we call it. Unless of course we change the directory to pg_xlogs_do_not_ever_delete, but I doubt that would help either. Sincerely, Joshua D. Drake > ---------------------------(end of broadcast)--------------------------- > TIP 9: In versions below 8.0, the planner will ignore your desire to > choose an index scan if your joining column's datatypes do not > match > ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 274+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 274+ messages in thread
end of thread, other threads:[~2025-07-27 15:48 UTC | newest] Thread overview: 274+ messages (download: mbox mbox.gz follow: Atom feed) -- links below jump to the message on this page -- 2005-11-15 03:19 Major Problem, need help! Can't run our website! ITS ONT Alcazar, Jose Aguedo C <[email protected]> 2005-11-15 03:48 ` Tim Allen <[email protected]> 2005-11-15 04:02 ` Tom Lane <[email protected]> 2005-11-15 04:32 ` Re: [HACKERS] Major Problem, need help! Can't run our Rod Taylor <[email protected]> 2005-11-15 04:48 ` Re: [HACKERS] Major Problem, need help! Can't run our Tom Lane <[email protected]> 2005-11-15 06:28 ` Re: [HACKERS] Major Problem, need help! Can't run our Jonah H. Harris <[email protected]> 2005-11-15 04:05 ` Christopher Kings-Lynne <[email protected]> 2005-11-15 04:17 ` Tom Lane <[email protected]> 2005-11-15 23:27 ` Trent Shipley <[email protected]> 2005-11-16 01:27 ` Joshua D. Drake <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-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