agora inbox for [email protected]
help / color / mirror / Atom feedShould the docs have a warning about pg_stat_reset()?
270+ messages / 6 participants
[nested] [flat]
* Should the docs have a warning about pg_stat_reset()?
@ 2019-03-26 12:53 David Rowley <[email protected]>
2019-03-26 15:28 ` Re: Should the docs have a warning about pg_stat_reset()? Euler Taveira <[email protected]>
0 siblings, 1 reply; 270+ messages in thread
From: David Rowley @ 2019-03-26 12:53 UTC (permalink / raw)
To: PostgreSQL Hackers <[email protected]>
As I mentioned in [1], I've had a few cases recently about auto-vacuum
not working. On the other thread, it was all about auto-vacuum being
configured to run too slowly. The other culprit for auto-vacuum not
working is when people periodically use pg_stat_reset().
The problem with pg_stat_reset() is that it zeros n_dead_tup and
n_mod_since_analyze. If say a user resets the stats on a monthly
basis then this can mean that tables that normally receive an
auto-vacuum any less frequently than once per month could never
receive an auto-vacuum... at least not until an anti-wraparound vacuum
gets hold of it.
The best I can think to do to try and avoid this is to put a giant
WARNING in the docs about either not using it or to at least run
ANALYZE after using it.
Does anyone else think this is a problem worth trying to solve?
[1] https://www.postgresql.org/message-id/[email protected]...
--
David Rowley http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
^ permalink raw reply [nested|flat] 270+ messages in thread
* Re: Should the docs have a warning about pg_stat_reset()?
2019-03-26 12:53 Should the docs have a warning about pg_stat_reset()? David Rowley <[email protected]>
@ 2019-03-26 15:28 ` Euler Taveira <[email protected]>
2019-03-27 21:28 ` Re: Should the docs have a warning about pg_stat_reset()? Peter Eisentraut <[email protected]>
0 siblings, 1 reply; 270+ messages in thread
From: Euler Taveira @ 2019-03-26 15:28 UTC (permalink / raw)
To: David Rowley <[email protected]>; +Cc: PostgreSQL Hackers <[email protected]>
Em ter, 26 de mar de 2019 às 09:54, David Rowley
<[email protected]> escreveu:
>
> As I mentioned in [1], I've had a few cases recently about auto-vacuum
> not working. On the other thread, it was all about auto-vacuum being
> configured to run too slowly. The other culprit for auto-vacuum not
> working is when people periodically use pg_stat_reset().
>
> The problem with pg_stat_reset() is that it zeros n_dead_tup and
> n_mod_since_analyze. If say a user resets the stats on a monthly
> basis then this can mean that tables that normally receive an
> auto-vacuum any less frequently than once per month could never
> receive an auto-vacuum... at least not until an anti-wraparound vacuum
> gets hold of it.
>
It seems a bug^H^H^H new feature. The problem is if you keep resetting
statistic before reaching an ANALYZE threshold. In this case,
autoVACUUM was never triggered because we don't have stats. The
consequence is a huge bloat.
> The best I can think to do to try and avoid this is to put a giant
> WARNING in the docs about either not using it or to at least run
> ANALYZE after using it.
>
+1. I am afraid it is not sufficient.
> Does anyone else think this is a problem worth trying to solve?
>
I don't remember why we didn't consider table without stats to be
ANALYZEd. Isn't it the case to fix autovacuum? Analyze
autovacuum_count + vacuum_count = 0?
If at least autovacuum was also time-based, it should mitigate the
lack of statistic.
--
Euler Taveira Timbira -
http://www.timbira.com.br/
PostgreSQL: Consultoria, Desenvolvimento, Suporte 24x7 e Treinamento
^ permalink raw reply [nested|flat] 270+ messages in thread
* Re: Should the docs have a warning about pg_stat_reset()?
2019-03-26 12:53 Should the docs have a warning about pg_stat_reset()? David Rowley <[email protected]>
2019-03-26 15:28 ` Re: Should the docs have a warning about pg_stat_reset()? Euler Taveira <[email protected]>
@ 2019-03-27 21:28 ` Peter Eisentraut <[email protected]>
2019-03-27 21:33 ` Re: Should the docs have a warning about pg_stat_reset()? Alvaro Herrera <[email protected]>
0 siblings, 1 reply; 270+ messages in thread
From: Peter Eisentraut @ 2019-03-27 21:28 UTC (permalink / raw)
To: Euler Taveira <[email protected]>; David Rowley <[email protected]>; +Cc: PostgreSQL Hackers <[email protected]>
On 2019-03-26 16:28, Euler Taveira wrote:
> I don't remember why we didn't consider table without stats to be
> ANALYZEd. Isn't it the case to fix autovacuum? Analyze
> autovacuum_count + vacuum_count = 0?
When the autovacuum system was introduced, we didn't have those columns.
But now it seems to make sense that a table with autoanalyze_count +
analyze_count = 0 should be a candidate for autovacuum even if the write
statistics are zero. Obviously, this would have the effect that a
pg_stat_reset() causes an immediate autovacuum for all tables, so maybe
it's not quite that simple.
--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
^ permalink raw reply [nested|flat] 270+ messages in thread
* Re: Should the docs have a warning about pg_stat_reset()?
2019-03-26 12:53 Should the docs have a warning about pg_stat_reset()? David Rowley <[email protected]>
2019-03-26 15:28 ` Re: Should the docs have a warning about pg_stat_reset()? Euler Taveira <[email protected]>
2019-03-27 21:28 ` Re: Should the docs have a warning about pg_stat_reset()? Peter Eisentraut <[email protected]>
@ 2019-03-27 21:33 ` Alvaro Herrera <[email protected]>
2019-03-27 23:49 ` Re: Should the docs have a warning about pg_stat_reset()? David Rowley <[email protected]>
0 siblings, 1 reply; 270+ messages in thread
From: Alvaro Herrera @ 2019-03-27 21:33 UTC (permalink / raw)
To: Peter Eisentraut <[email protected]>; +Cc: Euler Taveira <[email protected]>; David Rowley <[email protected]>; PostgreSQL Hackers <[email protected]>
On 2019-Mar-27, Peter Eisentraut wrote:
> On 2019-03-26 16:28, Euler Taveira wrote:
> > I don't remember why we didn't consider table without stats to be
> > ANALYZEd. Isn't it the case to fix autovacuum? Analyze
> > autovacuum_count + vacuum_count = 0?
>
> When the autovacuum system was introduced, we didn't have those columns.
> But now it seems to make sense that a table with autoanalyze_count +
> analyze_count = 0 should be a candidate for autovacuum even if the write
> statistics are zero. Obviously, this would have the effect that a
> pg_stat_reset() causes an immediate autovacuum for all tables, so maybe
> it's not quite that simple.
I'd say it would make them a candidate for auto-analyze; upon completion
of that, there's sufficient data to determine whether auto-vacuum is
needed or not. This sounds like a sensible idea to me.
--
Álvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
^ permalink raw reply [nested|flat] 270+ messages in thread
* Re: Should the docs have a warning about pg_stat_reset()?
2019-03-26 12:53 Should the docs have a warning about pg_stat_reset()? David Rowley <[email protected]>
2019-03-26 15:28 ` Re: Should the docs have a warning about pg_stat_reset()? Euler Taveira <[email protected]>
2019-03-27 21:28 ` Re: Should the docs have a warning about pg_stat_reset()? Peter Eisentraut <[email protected]>
2019-03-27 21:33 ` Re: Should the docs have a warning about pg_stat_reset()? Alvaro Herrera <[email protected]>
@ 2019-03-27 23:49 ` David Rowley <[email protected]>
2019-03-29 11:59 ` Re: Should the docs have a warning about pg_stat_reset()? Robert Haas <[email protected]>
0 siblings, 1 reply; 270+ messages in thread
From: David Rowley @ 2019-03-27 23:49 UTC (permalink / raw)
To: Alvaro Herrera <[email protected]>; +Cc: Peter Eisentraut <[email protected]>; Euler Taveira <[email protected]>; PostgreSQL Hackers <[email protected]>
On Thu, 28 Mar 2019 at 10:33, Alvaro Herrera <[email protected]> wrote:
>
> On 2019-Mar-27, Peter Eisentraut wrote:
>
> > On 2019-03-26 16:28, Euler Taveira wrote:
> > > I don't remember why we didn't consider table without stats to be
> > > ANALYZEd. Isn't it the case to fix autovacuum? Analyze
> > > autovacuum_count + vacuum_count = 0?
> >
> > When the autovacuum system was introduced, we didn't have those columns.
> > But now it seems to make sense that a table with autoanalyze_count +
> > analyze_count = 0 should be a candidate for autovacuum even if the write
> > statistics are zero. Obviously, this would have the effect that a
> > pg_stat_reset() causes an immediate autovacuum for all tables, so maybe
> > it's not quite that simple.
>
> I'd say it would make them a candidate for auto-analyze; upon completion
> of that, there's sufficient data to determine whether auto-vacuum is
> needed or not. This sounds like a sensible idea to me.
Yeah, analyze, not vacuum. It is a bit scary to add new ways for
auto-vacuum to suddenly have a lot of work to do. When all workers
are busy it can lead to neglect of other duties. It's true that there
won't be much in the way of routine vacuuming work for the database
the stats were just reset on, as of course, all the n_dead_tup
counters were just reset. However, it could starve other databases of
vacuum attention. Anti-wraparound vacuums on the current database may
get neglected too.
I'm not saying let's not do it, I'm just saying we need to think of
what bad things could happen as a result of such a change.
--
David Rowley http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
^ permalink raw reply [nested|flat] 270+ messages in thread
* Re: Should the docs have a warning about pg_stat_reset()?
2019-03-26 12:53 Should the docs have a warning about pg_stat_reset()? David Rowley <[email protected]>
2019-03-26 15:28 ` Re: Should the docs have a warning about pg_stat_reset()? Euler Taveira <[email protected]>
2019-03-27 21:28 ` Re: Should the docs have a warning about pg_stat_reset()? Peter Eisentraut <[email protected]>
2019-03-27 21:33 ` Re: Should the docs have a warning about pg_stat_reset()? Alvaro Herrera <[email protected]>
2019-03-27 23:49 ` Re: Should the docs have a warning about pg_stat_reset()? David Rowley <[email protected]>
@ 2019-03-29 11:59 ` Robert Haas <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Robert Haas @ 2019-03-29 11:59 UTC (permalink / raw)
To: David Rowley <[email protected]>; +Cc: Alvaro Herrera <[email protected]>; Peter Eisentraut <[email protected]>; Euler Taveira <[email protected]>; PostgreSQL Hackers <[email protected]>
On Wed, Mar 27, 2019 at 7:49 PM David Rowley
<[email protected]> wrote:
> Yeah, analyze, not vacuum. It is a bit scary to add new ways for
> auto-vacuum to suddenly have a lot of work to do. When all workers
> are busy it can lead to neglect of other duties. It's true that there
> won't be much in the way of routine vacuuming work for the database
> the stats were just reset on, as of course, all the n_dead_tup
> counters were just reset. However, it could starve other databases of
> vacuum attention. Anti-wraparound vacuums on the current database may
> get neglected too.
>
> I'm not saying let's not do it, I'm just saying we need to think of
> what bad things could happen as a result of such a change.
+1. I think that if we documented that pg_stat_reset() is going to
trigger an auto-analyze of every table in your system, we'd have some
people who didn't read the documentation and unleashed a storm of
auto-analyze activity, and other people who did read the documentation
and then intentionally used it to unleash a storm of auto-analyze
activity. Neither sounds that great.
I really wish somebody had the time and energy to put some serious
work on the problem of autovacuum scheduling in general. Our current
algorithm is a huge improvement over what what we had before 8.3, but
that was a decade ago. This particular issue strikes me as something
that is likely to be hard to solve with an isolated tweak.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source
@ 2025-07-27 15:48 Lukas Fittl <[email protected]>
0 siblings, 0 replies; 270+ messages in thread
From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw)
In passing also reduce the per-loop overhead caused by repeated divisions
in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large,
instead diff first and then turn it into nanosecs.
---
src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------
src/include/portability/instr_time.h | 30 ++++++++-----
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index a5621251afc..b77ef2063b6 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -16,6 +16,7 @@ static const char *progname;
static unsigned int test_duration = 3;
static double max_rprct = 99.99;
+static bool fast_timing = false;
/* record duration in powers of 2 nanoseconds */
static long long int histogram[32];
@@ -56,6 +57,7 @@ handle_args(int argc, char *argv[])
static struct option long_options[] = {
{"duration", required_argument, NULL, 'd'},
{"cutoff", required_argument, NULL, 'c'},
+ {"fast", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
@@ -68,7 +70,7 @@ handle_args(int argc, char *argv[])
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
- printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
+ printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
@@ -78,7 +80,7 @@ handle_args(int argc, char *argv[])
}
}
- while ((option = getopt_long(argc, argv, "d:c:",
+ while ((option = getopt_long(argc, argv, "d:c:f:",
long_options, &optindex)) != -1)
{
switch (option)
@@ -125,6 +127,10 @@ handle_args(int argc, char *argv[])
}
break;
+ case 'f':
+ fast_timing = true;
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
@@ -155,11 +161,31 @@ test_timing(unsigned int duration)
uint64 total_time;
int64 time_elapsed = 0;
uint64 loop_count = 0;
- uint64 prev,
- cur;
instr_time start_time,
end_time,
- temp;
+ prev,
+ cur;
+ char *time_source = NULL;
+ bool fast_timing_used = false;
+
+ INSTR_TIME_INITIALIZE();
+
+#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
+ if (fast_timing && has_rdtsc)
+ {
+ time_source = "RDTSC";
+ fast_timing_used = true;
+ }
+ else if (has_rdtscp)
+ time_source = "RDTSCP";
+ else
+ time_source = PG_INSTR_CLOCK_NAME;
+#else
+ time_source = PG_INSTR_CLOCK_NAME;
+#endif
+ if (fast_timing && !fast_timing_used)
+ printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n"));
+ printf(_("Time source: %s\n"), time_source);
/*
* Pre-zero the statistics data structures. They're already zero by
@@ -173,8 +199,11 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
- INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_NANOSEC(start_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(start_time);
+ else
+ INSTR_TIME_SET_CURRENT(start_time);
+ cur = start_time;
while (time_elapsed < total_time)
{
@@ -182,9 +211,11 @@ test_timing(unsigned int duration)
bits;
prev = cur;
- INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_NANOSEC(temp);
- diff = cur - prev;
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(cur);
+ else
+ INSTR_TIME_SET_CURRENT(cur);
+ diff = INSTR_TIME_DIFF_NANOSEC(cur, prev);
/* Did time go backwards? */
if (unlikely(diff < 0))
@@ -217,11 +248,13 @@ test_timing(unsigned int duration)
largest_diff_count++;
loop_count++;
- INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
+ time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time);
}
- INSTR_TIME_SET_CURRENT(end_time);
+ if (fast_timing)
+ INSTR_TIME_SET_CURRENT_FAST(end_time);
+ else
+ INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index e2e339a0c4f..f02296f1026 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow;
*/
#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)"
#elif defined(CLOCK_MONOTONIC)
#define PG_INSTR_CLOCK CLOCK_MONOTONIC
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)"
#else
#define PG_INSTR_CLOCK CLOCK_REALTIME
+#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)"
#endif
#if defined(__x86_64__) && defined(__linux__)
@@ -174,7 +177,7 @@ pg_get_ticks(void)
}
static inline int64_t
-pg_ticks_to_ns(instr_time t)
+pg_ticks_to_ns(int64 ticks)
{
/*
* Would multiplication overflow? If so perform computation in two parts.
@@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t)
*/
int64 ns = 0;
- if (unlikely(t.ticks > max_ticks_no_overflow))
+ if (unlikely(ticks > max_ticks_no_overflow))
{
/*
* Compute how often the maximum number of ticks fits completely into
@@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t)
* value. In a 2nd step we adjust the number of elapsed ticks and
* convert the remaining ticks.
*/
- int64 count = t.ticks / max_ticks_no_overflow;
+ int64 count = ticks / max_ticks_no_overflow;
int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
ns = max_ns * count;
@@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t)
* Subtract the ticks that we now already accounted for, so that they
* don't get counted twice.
*/
- t.ticks -= count * max_ticks_no_overflow;
- Assert(t.ticks >= 0);
+ ticks -= count * max_ticks_no_overflow;
+ Assert(ticks >= 0);
}
- ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
+ ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION;
return ns;
}
@@ -226,14 +229,14 @@ pg_initialize_get_ticks()
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_get_ticks())
-#define INSTR_TIME_GET_NANOSEC(t) \
- pg_ticks_to_ns(t)
-
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ (pg_ticks_to_ns(ticks))
#else /* WIN32 */
/* Use QueryPerformanceCounter() */
+#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter"
/* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */
static inline instr_time
@@ -265,8 +268,8 @@ GetTimerFrequency(void)
#define INSTR_TIME_SET_CURRENT(t) \
((t) = pg_query_performance_counter())
-#define INSTR_TIME_GET_NANOSEC(t) \
- ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
+#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \
+ ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency())))
#endif /* WIN32 */
@@ -285,9 +288,14 @@ GetTimerFrequency(void)
#define INSTR_TIME_SUBTRACT(x,y) \
((x).ticks -= (y).ticks)
+#define INSTR_TIME_DIFF_NANOSEC(x,y) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks))
+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
((x).ticks += (y).ticks - (z).ticks)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks))
#define INSTR_TIME_GET_DOUBLE(t) \
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
--
2.47.3
--vtqqtrpooseurzip--
^ permalink raw reply [nested|flat] 270+ messages in thread
end of thread, other threads:[~2025-07-27 15:48 UTC | newest]
Thread overview: 270+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2019-03-26 12:53 Should the docs have a warning about pg_stat_reset()? David Rowley <[email protected]>
2019-03-26 15:28 ` Euler Taveira <[email protected]>
2019-03-27 21:28 ` Peter Eisentraut <[email protected]>
2019-03-27 21:33 ` Alvaro Herrera <[email protected]>
2019-03-27 23:49 ` David Rowley <[email protected]>
2019-03-29 11:59 ` Robert Haas <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]>
2025-07-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