agora inbox for [email protected]help / color / mirror / Atom feed
psql \l to accept patterns 279+ messages / 8 participants [nested] [flat]
* psql \l to accept patterns @ 2013-01-07 12:14 Peter Eisentraut <[email protected]> 0 siblings, 4 replies; 279+ messages in thread From: Peter Eisentraut @ 2013-01-07 12:14 UTC (permalink / raw) To: pgsql-hackers Here is a patch for psql's \l command to accept patterns, like \d commands do. While at it, I also added an "S" option to show system objects and removed system objects from the default display. This might be a bit controversial, but it's how it was decided some time ago that the \d commands should act. -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers Attachments: [text/x-patch] pg-psql-l-pattern.patch (5.3K, ../../[email protected]/2-pg-psql-l-pattern.patch) download | inline diff: diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml index c41593c..a9770c0 100644 --- a/doc/src/sgml/ref/psql-ref.sgml +++ b/doc/src/sgml/ref/psql-ref.sgml @@ -1689,16 +1689,19 @@ <title>Meta-Commands</title> <varlistentry> - <term><literal>\l</literal> (or <literal>\list</literal>)</term> - <term><literal>\l+</literal> (or <literal>\list+</literal>)</term> + <term><literal>\l[S+]</literal> (or <literal>\list[S+]</literal>)</term> <listitem> <para> - List the names, owners, character set encodings, and access privileges - of all the databases in the server. + List the databases in the server and show they names, owners, character + set encodings, and access privileges. + If <replaceable class="parameter">pattern</replaceable> is specified, + only databases whose names match the pattern are listed. If <literal>+</literal> is appended to the command name, database - sizes, default tablespaces, and descriptions are also displayed. - (Size information is only available for databases that the current - user can connect to.) + sizes, default tablespaces, and descriptions are also displayed. (Size + information is only available for databases that the current user can + connect to.) By default, only user-created databases are shown; supply + a pattern or the <literal>S</literal> modifier to include system + objects. </para> </listitem> </varlistentry> diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c index 59f8b03..aea0903 100644 --- a/src/bin/psql/command.c +++ b/src/bin/psql/command.c @@ -804,10 +804,27 @@ static bool do_edit(const char *filename_arg, PQExpBuffer query_buf, } /* \l is list databases */ - else if (strcmp(cmd, "l") == 0 || strcmp(cmd, "list") == 0) - success = listAllDbs(false); - else if (strcmp(cmd, "l+") == 0 || strcmp(cmd, "list+") == 0) - success = listAllDbs(true); + else if (strcmp(cmd, "l") == 0 || strcmp(cmd, "list") == 0 || + strcmp(cmd, "l+") == 0 || strcmp(cmd, "list+") == 0 || + strcmp(cmd, "lS") == 0 || strcmp(cmd, "listS") == 0 || + strcmp(cmd, "l+S") == 0 || strcmp(cmd, "list+S") == 0 || + strcmp(cmd, "lS+") == 0 || strcmp(cmd, "listS+") == 0) + { + char *pattern; + bool show_verbose, + show_system; + + pattern = psql_scan_slash_option(scan_state, + OT_NORMAL, NULL, true); + + show_verbose = strchr(cmd, '+') ? true : false; + show_system = strchr(cmd, 'S') ? true : false; + + success = listAllDbs(pattern, show_verbose, show_system); + + if (pattern) + free(pattern); + } /* * large object things diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 87174cc..c6ac40c 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -637,7 +637,7 @@ static bool describeOneTSConfig(const char *oid, const char *nspname, * for \l, \list, and -l switch */ bool -listAllDbs(bool verbose) +listAllDbs(const char *pattern, bool verbose, bool showSystem) { PGresult *res; PQExpBufferData buf; @@ -680,6 +680,14 @@ static bool describeOneTSConfig(const char *oid, const char *nspname, if (verbose && pset.sversion >= 80000) appendPQExpBuffer(&buf, " JOIN pg_catalog.pg_tablespace t on d.dattablespace = t.oid\n"); + + if (pattern) + processSQLNamePattern(pset.db, &buf, pattern, false, false, + NULL, "d.datname", NULL, NULL); + + if (!showSystem && !pattern) + appendPQExpBuffer(&buf, "WHERE d.datname NOT IN ('postgres', 'template0', 'template1')\n"); + appendPQExpBuffer(&buf, "ORDER BY 1;"); res = PSQLexec(buf.data, false); termPQExpBuffer(&buf); diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h index 9e71a88..721c363 100644 --- a/src/bin/psql/describe.h +++ b/src/bin/psql/describe.h @@ -55,7 +55,7 @@ extern bool listTSDictionaries(const char *pattern, bool verbose); extern bool listTSTemplates(const char *pattern, bool verbose); /* \l */ -extern bool listAllDbs(bool verbose); +extern bool listAllDbs(const char *pattern, bool verbose, bool showSystem); /* \dt, \di, \ds, \dS, etc. */ extern bool listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSystem); diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c index 1070bc5..ab32b32 100644 --- a/src/bin/psql/help.c +++ b/src/bin/psql/help.c @@ -230,7 +230,7 @@ fprintf(output, _(" \\dE[S+] [PATTERN] list foreign tables\n")); fprintf(output, _(" \\dx[+] [PATTERN] list extensions\n")); fprintf(output, _(" \\dy [PATTERN] list event triggers\n")); - fprintf(output, _(" \\l[+] list all databases\n")); + fprintf(output, _(" \\l[S+] [PATTERN] list databases\n")); fprintf(output, _(" \\sf[+] FUNCNAME show a function's definition\n")); fprintf(output, _(" \\z [PATTERN] same as \\dp\n")); fprintf(output, "\n"); diff --git a/src/bin/psql/startup.c b/src/bin/psql/startup.c index a59f45b..1ed8e66 100644 --- a/src/bin/psql/startup.c +++ b/src/bin/psql/startup.c @@ -260,7 +260,7 @@ static void parse_psql_options(int argc, char *argv[], if (!options.no_psqlrc) process_psqlrc(argv[0]); - success = listAllDbs(false); + success = listAllDbs(NULL, false, true); PQfinish(pset.db); exit(success ? EXIT_SUCCESS : EXIT_FAILURE); } ^ permalink raw reply [nested|flat] 279+ messages in thread
* Re: psql \l to accept patterns @ 2013-01-07 19:19 Fabrízio de Royes Mello <[email protected]> parent: Peter Eisentraut <[email protected]> 3 siblings, 0 replies; 279+ messages in thread From: Fabrízio de Royes Mello @ 2013-01-07 19:19 UTC (permalink / raw) To: Peter Eisentraut <[email protected]>; +Cc: pgsql-hackers On Mon, Jan 7, 2013 at 10:14 AM, Peter Eisentraut <[email protected]> wrote: > Here is a patch for psql's \l command to accept patterns, like \d > commands do. While at it, I also added an "S" option to show system > objects and removed system objects from the default display. This might > be a bit controversial, but it's how it was decided some time ago that > the \d commands should act. > > I applied the attached patch to the current master branch and everything is ok. When build all works fine too... and I do some tests: 1) Now '\l' list only regular databases postgres=# \l List of databases Name | Owner | Encoding | Collate | Ctype | Access privileges ------+-------+----------+---------+-------+------------------- (0 rows) postgres=# CREATE DATABASE fabrizio; CREATE DATABASE postgres=# \l List of databases Name | Owner | Encoding | Collate | Ctype | Access privileges ----------+----------+----------+-------------+-------------+------------------- fabrizio | fabrizio | UTF8 | en_US.UTF-8 | en_US.UTF-8 | (1 row) postgres=# \l+ List of databases Name | Owner | Encoding | Collate | Ctype | Access privileges | Size | Tablespace | Description ----------+----------+----------+-------------+-------------+-------------------+---------+------------+------------- fabrizio | fabrizio | UTF8 | en_US.UTF-8 | en_US.UTF-8 | | 5945 kB | pg_default | (1 row) postgres=# DROP DATABASE fabrizio; DROP DATABASE postgres=# \l List of databases Name | Owner | Encoding | Collate | Ctype | Access privileges ------+-------+----------+---------+-------+------------------- (0 rows) 2) The new sub-command '\lS' list regular and systems databases postgres=# \lS List of databases Name | Owner | Encoding | Collate | Ctype | Access privileges -----------+----------+----------+-------------+-------------+----------------------- postgres | fabrizio | UTF8 | en_US.UTF-8 | en_US.UTF-8 | template0 | fabrizio | UTF8 | en_US.UTF-8 | en_US.UTF-8 | =c/fabrizio + | | | | | fabrizio=CTc/fabrizio template1 | fabrizio | UTF8 | en_US.UTF-8 | en_US.UTF-8 | =c/fabrizio + | | | | | fabrizio=CTc/fabrizio (3 rows) postgres=# CREATE DATABASE fabrizio; CREATE DATABASE postgres=# \lS List of databases Name | Owner | Encoding | Collate | Ctype | Access privileges -----------+----------+----------+-------------+-------------+----------------------- fabrizio | fabrizio | UTF8 | en_US.UTF-8 | en_US.UTF-8 | postgres | fabrizio | UTF8 | en_US.UTF-8 | en_US.UTF-8 | template0 | fabrizio | UTF8 | en_US.UTF-8 | en_US.UTF-8 | =c/fabrizio + | | | | | fabrizio=CTc/fabrizio template1 | fabrizio | UTF8 | en_US.UTF-8 | en_US.UTF-8 | =c/fabrizio + | | | | | fabrizio=CTc/fabrizio (4 rows) postgres=# \lS+ List of databases Name | Owner | Encoding | Collate | Ctype | Access privileges | Size | Tablespace | Description -----------+----------+----------+-------------+-------------+-----------------------+---------+------------+-------------------------------- ------------ fabrizio | fabrizio | UTF8 | en_US.UTF-8 | en_US.UTF-8 | | 5945 kB | pg_default | postgres | fabrizio | UTF8 | en_US.UTF-8 | en_US.UTF-8 | | 6041 kB | pg_default | default administrative connecti on database template0 | fabrizio | UTF8 | en_US.UTF-8 | en_US.UTF-8 | =c/fabrizio +| 5945 kB | pg_default | unmodifiable empty database | | | | | fabrizio=CTc/fabrizio | | | template1 | fabrizio | UTF8 | en_US.UTF-8 | en_US.UTF-8 | =c/fabrizio +| 5945 kB | pg_default | default template for new databa ses | | | | | fabrizio=CTc/fabrizio | | | (4 rows) postgres=# DROP DATABASE fabrizio ; DROP DATABASE postgres=# \lS List of databases Name | Owner | Encoding | Collate | Ctype | Access privileges -----------+----------+----------+-------------+-------------+----------------------- postgres | fabrizio | UTF8 | en_US.UTF-8 | en_US.UTF-8 | template0 | fabrizio | UTF8 | en_US.UTF-8 | en_US.UTF-8 | =c/fabrizio + | | | | | fabrizio=CTc/fabrizio template1 | fabrizio | UTF8 | en_US.UTF-8 | en_US.UTF-8 | =c/fabrizio + | | | | | fabrizio=CTc/fabrizio (3 rows) 3) Now '\l[S+] [pattern]' works: postgres=# CREATE DATABASE fabrizio; CRECREATE DATABASE postgres=# CREATE DATABASE postgis; CREATE DATABASE postgres=# CREATE DATABASE mytemplate; CREATE DATABASE postgres=# \l fab* List of databases Name | Owner | Encoding | Collate | Ctype | Access privileges ----------+----------+----------+-------------+-------------+------------------- fabrizio | fabrizio | UTF8 | en_US.UTF-8 | en_US.UTF-8 | (1 row) postgres=# \l post* List of databases Name | Owner | Encoding | Collate | Ctype | Access privileges ----------+----------+----------+-------------+-------------+------------------- postgis | fabrizio | UTF8 | en_US.UTF-8 | en_US.UTF-8 | postgres | fabrizio | UTF8 | en_US.UTF-8 | en_US.UTF-8 | (2 rows) postgres=# \l *template* List of databases Name | Owner | Encoding | Collate | Ctype | Access privileges ------------+----------+----------+-------------+-------------+----------------------- mytemplate | fabrizio | UTF8 | en_US.UTF-8 | en_US.UTF-8 | template0 | fabrizio | UTF8 | en_US.UTF-8 | en_US.UTF-8 | =c/fabrizio + | | | | | fabrizio=CTc/fabrizio template1 | fabrizio | UTF8 | en_US.UTF-8 | en_US.UTF-8 | =c/fabrizio + | | | | | fabrizio=CTc/fabrizio (3 rows) postgres=# \l *template List of databases Name | Owner | Encoding | Collate | Ctype | Access privileges ------------+----------+----------+-------------+-------------+------------------- mytemplate | fabrizio | UTF8 | en_US.UTF-8 | en_US.UTF-8 | (1 row) 4) By command line all works ok too... $ ./bin/psql -c "\l" List of databases Name | Owner | Encoding | Collate | Ctype | Access privileges ------------+----------+----------+-------------+-------------+------------------- fabrizio | fabrizio | UTF8 | en_US.UTF-8 | en_US.UTF-8 | mytemplate | fabrizio | UTF8 | en_US.UTF-8 | en_US.UTF-8 | postgis | fabrizio | UTF8 | en_US.UTF-8 | en_US.UTF-8 | (3 rows) $ ./bin/psql -c "\lS" List of databases Name | Owner | Encoding | Collate | Ctype | Access privileges ------------+----------+----------+-------------+-------------+----------------------- fabrizio | fabrizio | UTF8 | en_US.UTF-8 | en_US.UTF-8 | mytemplate | fabrizio | UTF8 | en_US.UTF-8 | en_US.UTF-8 | postgis | fabrizio | UTF8 | en_US.UTF-8 | en_US.UTF-8 | postgres | fabrizio | UTF8 | en_US.UTF-8 | en_US.UTF-8 | template0 | fabrizio | UTF8 | en_US.UTF-8 | en_US.UTF-8 | =c/fabrizio + | | | | | fabrizio=CTc/fabrizio template1 | fabrizio | UTF8 | en_US.UTF-8 | en_US.UTF-8 | =c/fabrizio + | | | | | fabrizio=CTc/fabrizio (6 rows) $ ./bin/psql -c "\l post*" List of databases Name | Owner | Encoding | Collate | Ctype | Access privileges ----------+----------+----------+-------------+-------------+------------------- postgis | fabrizio | UTF8 | en_US.UTF-8 | en_US.UTF-8 | postgres | fabrizio | UTF8 | en_US.UTF-8 | en_US.UTF-8 | (2 rows) 5) Docs and psql help was updated correctly. The attached patch is ok for me and ready for commit. Regards, -- Fabrízio de Royes Mello Consultoria/Coaching PostgreSQL >> Blog sobre TI: http://fabriziomello.blogspot.com >> Perfil Linkedin: http://br.linkedin.com/in/fabriziomello >> Twitter: http://twitter.com/fabriziomello ^ permalink raw reply [nested|flat] 279+ messages in thread
* Re: psql \l to accept patterns @ 2013-01-07 19:23 Alvaro Herrera <[email protected]> parent: Peter Eisentraut <[email protected]> 3 siblings, 0 replies; 279+ messages in thread From: Alvaro Herrera @ 2013-01-07 19:23 UTC (permalink / raw) To: Peter Eisentraut <[email protected]>; +Cc: pgsql-hackers Peter Eisentraut wrote: > Here is a patch for psql's \l command to accept patterns, like \d > commands do. While at it, I also added an "S" option to show system > objects and removed system objects from the default display. This might > be a bit controversial, but it's how it was decided some time ago that > the \d commands should act. How does this affect psql -l? Should it, for instance, hide system DBs? Accept an optional pattern? -- Álvaro Herrera http://www.2ndQuadrant.com/ PostgreSQL Development, 24x7 Support, Training & Services -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ^ permalink raw reply [nested|flat] 279+ messages in thread
* Re: psql \l to accept patterns @ 2013-01-07 20:53 Robert Haas <[email protected]> parent: Peter Eisentraut <[email protected]> 3 siblings, 2 replies; 279+ messages in thread From: Robert Haas @ 2013-01-07 20:53 UTC (permalink / raw) To: Peter Eisentraut <[email protected]>; +Cc: pgsql-hackers On Mon, Jan 7, 2013 at 7:14 AM, Peter Eisentraut <[email protected]> wrote: > Here is a patch for psql's \l command to accept patterns, like \d > commands do. While at it, I also added an "S" option to show system > objects and removed system objects from the default display. This might > be a bit controversial, but it's how it was decided some time ago that > the \d commands should act. -1 from me on that last bit. I don't think that you can really compare the postgres or template1 database to, say, the pg_toast schema. There's no real reason for people to ever care about objects in the pg_toast schema, but the same cannot be said about template1, which it's often necessary to connect to when running many of the commands (vacuumdb -a, etc.) we ship with our distribution. I think this will just be confusing to users without any real upside. -- Robert Haas EnterpriseDB: http://www.enterprisedb.com The Enterprise PostgreSQL Company -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ^ permalink raw reply [nested|flat] 279+ messages in thread
* Re: psql \l to accept patterns @ 2013-01-07 21:16 Tom Lane <[email protected]> parent: Robert Haas <[email protected]> 1 sibling, 0 replies; 279+ messages in thread From: Tom Lane @ 2013-01-07 21:16 UTC (permalink / raw) To: Robert Haas <[email protected]>; +Cc: Peter Eisentraut <[email protected]>; pgsql-hackers Robert Haas <[email protected]> writes: > On Mon, Jan 7, 2013 at 7:14 AM, Peter Eisentraut <[email protected]> wrote: >> Here is a patch for psql's \l command to accept patterns, like \d >> commands do. While at it, I also added an "S" option to show system >> objects and removed system objects from the default display. This might >> be a bit controversial, but it's how it was decided some time ago that >> the \d commands should act. > -1 from me on that last bit. I don't think that you can really > compare the postgres or template1 database to, say, the pg_toast > schema. There's no real reason for people to ever care about objects > in the pg_toast schema, but the same cannot be said about template1, > which it's often necessary to connect to when running many of the > commands (vacuumdb -a, etc.) we ship with our distribution. I think > this will just be confusing to users without any real upside. Suppressing the postgres DB is even worse. I think that it might be sensible to have an "S" option and define "system" DBs as those without datallowconn, which ordinarily would only hide template0. But I can't get real excited about that. People do need to know about the existence of template0 (for use in CREATE DATABASE ... TEMPLATE ...), which is not so true of, say, pg_temp_NNN schemas. The "it reduces clutter" argument also seems pretty weak if we're only hiding one database, or even three of them. On the whole I lean towards not adding this notion. regards, tom lane -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ^ permalink raw reply [nested|flat] 279+ messages in thread
* Re: psql \l to accept patterns @ 2013-01-07 22:14 Peter Eisentraut <[email protected]> parent: Robert Haas <[email protected]> 1 sibling, 1 reply; 279+ messages in thread From: Peter Eisentraut @ 2013-01-07 22:14 UTC (permalink / raw) To: Robert Haas <[email protected]>; +Cc: pgsql-hackers On 1/7/13 3:53 PM, Robert Haas wrote: > On Mon, Jan 7, 2013 at 7:14 AM, Peter Eisentraut <[email protected]> wrote: >> > Here is a patch for psql's \l command to accept patterns, like \d >> > commands do. While at it, I also added an "S" option to show system >> > objects and removed system objects from the default display. This might >> > be a bit controversial, but it's how it was decided some time ago that >> > the \d commands should act. > -1 from me on that last bit. I don't think that you can really > compare the postgres or template1 database to, say, the pg_toast > schema. There's no real reason for people to ever care about objects > in the pg_toast schema, but the same cannot be said about template1, > which it's often necessary to connect to when running many of the > commands (vacuumdb -a, etc.) we ship with our distribution. I think > this will just be confusing to users without any real upside. We removed showing system functions and operators from \df and \do without S. Those are needed all the time. This was controversial at the time, but it's the way it is now. The definition of "S", I suppose, is more like "is there after database is created", not "typical users care about these objects". -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ^ permalink raw reply [nested|flat] 279+ messages in thread
* Re: psql \l to accept patterns @ 2013-01-07 22:37 Robert Haas <[email protected]> parent: Peter Eisentraut <[email protected]> 0 siblings, 1 reply; 279+ messages in thread From: Robert Haas @ 2013-01-07 22:37 UTC (permalink / raw) To: Peter Eisentraut <[email protected]>; +Cc: pgsql-hackers On Mon, Jan 7, 2013 at 5:14 PM, Peter Eisentraut <[email protected]> wrote: > We removed showing system functions and operators from \df and \do > without S. Those are needed all the time. This was controversial at > the time, but it's the way it is now. The definition of "S", I suppose, > is more like "is there after database is created", not "typical users > care about these objects". System functions and operators are needed all the time, but so are system tables and views, and the old behavior was that the latter were suppressed by default and the former were included by default. So I consider that change to be well-justified on consistency grounds. There's a practical consideration, as well. Out of the box, there are 2400 entries for functions and 3 for databases. This means that the old \df behavior made it very hard to figure out what user-defined functions exist in your database, but there's no corresponding problem with \l. Finally, note that you can drop the postgres database (and everything else will still work) but you cannot drop pg_table_is_visible(oid), because, as the error message will inform you, it is required by the database system. If we make the postgres database undroppable, unrenamable, and strictly read-only, I will happily support a proposal to consider it a system object. Until then, it's no more a system object than the public schema - which, you will note, \dn has no compunctions about displaying, even without S. -- Robert Haas EnterpriseDB: http://www.enterprisedb.com The Enterprise PostgreSQL Company -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ^ permalink raw reply [nested|flat] 279+ messages in thread
* Re: psql \l to accept patterns @ 2013-01-09 04:36 Peter Eisentraut <[email protected]> parent: Robert Haas <[email protected]> 0 siblings, 1 reply; 279+ messages in thread From: Peter Eisentraut @ 2013-01-09 04:36 UTC (permalink / raw) To: Robert Haas <[email protected]>; +Cc: pgsql-hackers On Mon, 2013-01-07 at 17:37 -0500, Robert Haas wrote: > If we make the postgres database undroppable, unrenamable, and > strictly read-only, I will happily support a proposal to consider it a > system object. Until then, it's no more a system object than the > public schema - which, you will note, \dn has no compunctions about > displaying, even without S. Good point. What about the other suggestion about only displaying databases by default that you can connect to? -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ^ permalink raw reply [nested|flat] 279+ messages in thread
* Re: psql \l to accept patterns @ 2013-01-11 16:28 Robert Haas <[email protected]> parent: Peter Eisentraut <[email protected]> 0 siblings, 1 reply; 279+ messages in thread From: Robert Haas @ 2013-01-11 16:28 UTC (permalink / raw) To: Peter Eisentraut <[email protected]>; +Cc: pgsql-hackers On Tue, Jan 8, 2013 at 11:36 PM, Peter Eisentraut <[email protected]> wrote: > On Mon, 2013-01-07 at 17:37 -0500, Robert Haas wrote: >> If we make the postgres database undroppable, unrenamable, and >> strictly read-only, I will happily support a proposal to consider it a >> system object. Until then, it's no more a system object than the >> public schema - which, you will note, \dn has no compunctions about >> displaying, even without S. > > Good point. What about the other suggestion about only displaying > databases by default that you can connect to? I would tend not to adopt that suggestion, on the grounds that it has no obvious parallel with anything else psql hides by default. However, I don't feel quite as strongly about that case. -- Robert Haas EnterpriseDB: http://www.enterprisedb.com The Enterprise PostgreSQL Company -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ^ permalink raw reply [nested|flat] 279+ messages in thread
* Re: psql \l to accept patterns @ 2013-01-11 16:35 Stephen Frost <[email protected]> parent: Robert Haas <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Stephen Frost @ 2013-01-11 16:35 UTC (permalink / raw) To: Robert Haas <[email protected]>; +Cc: Peter Eisentraut <[email protected]>; pgsql-hackers * Robert Haas ([email protected]) wrote: > On Tue, Jan 8, 2013 at 11:36 PM, Peter Eisentraut <[email protected]> wrote: > > Good point. What about the other suggestion about only displaying > > databases by default that you can connect to? > > I would tend not to adopt that suggestion, on the grounds that it has > no obvious parallel with anything else psql hides by default. > However, I don't feel quite as strongly about that case. In the past, haven't we done this through the catalog tables themselves rather than hacking up psql..? pg_stats being a prime example? With the row-level-security discussion, there was talk about if we might be able to apply that capability to catalogs also. That strikes me as a better option/approach than doing any of this in one particular application (psql in this case) which connects to PG. tbh, I'm not entirely against excluding databases that don't allow *any* connections (key'd off datallowconns) to clear out template0/template1 from the default list, but I see that as different from "things I don't have permissions to". Thanks, Stephen Attachments: [application/pgp-signature] signature.asc (198B, ../../[email protected]/2-signature.asc) download ^ permalink raw reply [nested|flat] 279+ messages in thread
* Re: psql \l to accept patterns @ 2013-01-29 05:48 Peter Eisentraut <[email protected]> parent: Peter Eisentraut <[email protected]> 3 siblings, 1 reply; 279+ messages in thread From: Peter Eisentraut @ 2013-01-29 05:48 UTC (permalink / raw) To: pgsql-hackers On Mon, 2013-01-07 at 07:14 -0500, Peter Eisentraut wrote: > Here is a patch for psql's \l command to accept patterns, like \d > commands do. While at it, I also added an "S" option to show system > objects and removed system objects from the default display. This might > be a bit controversial, but it's how it was decided some time ago that > the \d commands should act. Most people didn't like the "S" option, so here is a revised patch that just adds the pattern support. -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers Attachments: [text/x-patch] pg-psql-l-pattern.patch (4.5K, ../../[email protected]/2-pg-psql-l-pattern.patch) download | inline diff: diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml index 4c87d8a..8d0095e 100644 --- a/doc/src/sgml/ref/psql-ref.sgml +++ b/doc/src/sgml/ref/psql-ref.sgml @@ -1689,12 +1689,13 @@ <title>Meta-Commands</title> <varlistentry> - <term><literal>\l</literal> (or <literal>\list</literal>)</term> - <term><literal>\l+</literal> (or <literal>\list+</literal>)</term> + <term><literal>\l[+]</literal> or <literal>\list[+]</literal></term> <listitem> <para> - List the names, owners, character set encodings, and access privileges - of all the databases in the server. + List the databases in the server and show their names, owners, + character set encodings, and access privileges. + If <replaceable class="parameter">pattern</replaceable> is specified, + only databases whose names match the pattern are listed. If <literal>+</literal> is appended to the command name, database sizes, default tablespaces, and descriptions are also displayed. (Size information is only available for databases that the current diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c index 20c45e2..e40f8b2 100644 --- a/src/bin/psql/command.c +++ b/src/bin/psql/command.c @@ -804,10 +804,22 @@ static bool do_edit(const char *filename_arg, PQExpBuffer query_buf, } /* \l is list databases */ - else if (strcmp(cmd, "l") == 0 || strcmp(cmd, "list") == 0) - success = listAllDbs(false); - else if (strcmp(cmd, "l+") == 0 || strcmp(cmd, "list+") == 0) - success = listAllDbs(true); + else if (strcmp(cmd, "l") == 0 || strcmp(cmd, "list") == 0 || + strcmp(cmd, "l+") == 0 || strcmp(cmd, "list+") == 0) + { + char *pattern; + bool show_verbose; + + pattern = psql_scan_slash_option(scan_state, + OT_NORMAL, NULL, true); + + show_verbose = strchr(cmd, '+') ? true : false; + + success = listAllDbs(pattern, show_verbose); + + if (pattern) + free(pattern); + } /* * large object things diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 8064a3d..046513d 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -641,7 +641,7 @@ static bool describeOneTSConfig(const char *oid, const char *nspname, * for \l, \list, and -l switch */ bool -listAllDbs(bool verbose) +listAllDbs(const char *pattern, bool verbose) { PGresult *res; PQExpBufferData buf; @@ -684,6 +684,11 @@ static bool describeOneTSConfig(const char *oid, const char *nspname, if (verbose && pset.sversion >= 80000) appendPQExpBuffer(&buf, " JOIN pg_catalog.pg_tablespace t on d.dattablespace = t.oid\n"); + + if (pattern) + processSQLNamePattern(pset.db, &buf, pattern, false, false, + NULL, "d.datname", NULL, NULL); + appendPQExpBuffer(&buf, "ORDER BY 1;"); res = PSQLexec(buf.data, false); termPQExpBuffer(&buf); diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h index 9e71a88..09b6237 100644 --- a/src/bin/psql/describe.h +++ b/src/bin/psql/describe.h @@ -55,7 +55,7 @@ extern bool listTSDictionaries(const char *pattern, bool verbose); extern bool listTSTemplates(const char *pattern, bool verbose); /* \l */ -extern bool listAllDbs(bool verbose); +extern bool listAllDbs(const char *pattern, bool verbose); /* \dt, \di, \ds, \dS, etc. */ extern bool listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSystem); diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c index fd7effa..2cbdd83 100644 --- a/src/bin/psql/help.c +++ b/src/bin/psql/help.c @@ -233,7 +233,7 @@ fprintf(output, _(" \\dE[S+] [PATTERN] list foreign tables\n")); fprintf(output, _(" \\dx[+] [PATTERN] list extensions\n")); fprintf(output, _(" \\dy [PATTERN] list event triggers\n")); - fprintf(output, _(" \\l[+] list all databases\n")); + fprintf(output, _(" \\l[+] [PATTERN] list databases\n")); fprintf(output, _(" \\sf[+] FUNCNAME show a function's definition\n")); fprintf(output, _(" \\z [PATTERN] same as \\dp\n")); fprintf(output, "\n"); diff --git a/src/bin/psql/startup.c b/src/bin/psql/startup.c index a59f45b..5cb6b5f 100644 --- a/src/bin/psql/startup.c +++ b/src/bin/psql/startup.c @@ -260,7 +260,7 @@ static void parse_psql_options(int argc, char *argv[], if (!options.no_psqlrc) process_psqlrc(argv[0]); - success = listAllDbs(false); + success = listAllDbs(NULL, false); PQfinish(pset.db); exit(success ? EXIT_SUCCESS : EXIT_FAILURE); } ^ permalink raw reply [nested|flat] 279+ messages in thread
* Re: psql \l to accept patterns @ 2013-01-29 15:20 Satoshi Nagayasu <[email protected]> parent: Peter Eisentraut <[email protected]> 0 siblings, 1 reply; 279+ messages in thread From: Satoshi Nagayasu @ 2013-01-29 15:20 UTC (permalink / raw) To: Peter Eisentraut <[email protected]>; +Cc: pgsql-hackers Hi, I have tried this patch. https://commitfest.postgresql.org/action/patch_view?id=1051 2013/01/29 14:48, Peter Eisentraut wrote: > On Mon, 2013-01-07 at 07:14 -0500, Peter Eisentraut wrote: >> Here is a patch for psql's \l command to accept patterns, like \d >> commands do. While at it, I also added an "S" option to show system >> objects and removed system objects from the default display. This might >> be a bit controversial, but it's how it was decided some time ago that >> the \d commands should act. > > Most people didn't like the "S" option, so here is a revised patch that > just adds the pattern support. It seems working well with the latest git master. I think it's good enough to be committed. BTW, is there any good place to put new regression test for the psql command? I couldn't find it out. Any comment or suggestion? Regards, -- Satoshi Nagayasu <[email protected]> Uptime Technologies, LLC. http://www.uptime.jp -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ^ permalink raw reply [nested|flat] 279+ messages in thread
* Re: psql \l to accept patterns @ 2013-01-29 15:34 Tom Lane <[email protected]> parent: Satoshi Nagayasu <[email protected]> 0 siblings, 1 reply; 279+ messages in thread From: Tom Lane @ 2013-01-29 15:34 UTC (permalink / raw) To: Satoshi Nagayasu <[email protected]>; +Cc: Peter Eisentraut <[email protected]>; pgsql-hackers Satoshi Nagayasu <[email protected]> writes: >> On Mon, 2013-01-07 at 07:14 -0500, Peter Eisentraut wrote: >>> Here is a patch for psql's \l command to accept patterns, like \d > BTW, is there any good place to put new regression test for the psql > command? I couldn't find it out. As far as a test for this specific feature goes, I'd be against adding one, because it'd be likely to result in random failures in "make installcheck" mode, depending on what other databases are in the installation. More generally, we've tended to put tests of \d-style psql features together with the relevant backend-side tests. The proposed patch to add \gset adds a separate regression test file specifically for psql. I've been debating whether that was worth committing; but if there's near-term interest in adding any more tests for psql features that aren't closely connected to backend features, maybe it's worth having such a file. regards, tom lane -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ^ permalink raw reply [nested|flat] 279+ messages in thread
* Re: psql \l to accept patterns @ 2013-01-30 03:33 Satoshi Nagayasu <[email protected]> parent: Tom Lane <[email protected]> 0 siblings, 1 reply; 279+ messages in thread From: Satoshi Nagayasu @ 2013-01-30 03:33 UTC (permalink / raw) To: Tom Lane <[email protected]>; +Cc: Peter Eisentraut <[email protected]>; pgsql-hackers (2013/01/30 0:34), Tom Lane wrote: > Satoshi Nagayasu <[email protected]> writes: >>> On Mon, 2013-01-07 at 07:14 -0500, Peter Eisentraut wrote: >>>> Here is a patch for psql's \l command to accept patterns, like \d > >> BTW, is there any good place to put new regression test for the psql >> command? I couldn't find it out. > > As far as a test for this specific feature goes, I'd be against adding > one, because it'd be likely to result in random failures in "make > installcheck" mode, depending on what other databases are in the > installation. > > More generally, we've tended to put tests of \d-style psql features > together with the relevant backend-side tests. Yes, I think so too. First of all, I was looking for some regression tests for CREATE/ALTER/DROP DATABASE commands, but I couldn't find them in the test/regress/sql/ directory. So, I asked the question. I guess these database tests are in pg_regress.c. Right? > The proposed patch to add \gset adds a separate regression test file > specifically for psql. I've been debating whether that was worth > committing; but if there's near-term interest in adding any more tests > for psql features that aren't closely connected to backend features, > maybe it's worth having such a file. Personally, I'm interested in having regression tests whatever the target is, because software tends to be more complicated. So, if we reach consensus to have dedicated tests for the psql command (or other client-side commands), I wish to contribute to it. Regards, -- Satoshi Nagayasu <[email protected]> Uptime Technologies, LLC. http://www.uptime.jp -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ^ permalink raw reply [nested|flat] 279+ messages in thread
* Re: psql \l to accept patterns @ 2013-01-30 03:44 Tom Lane <[email protected]> parent: Satoshi Nagayasu <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Tom Lane @ 2013-01-30 03:44 UTC (permalink / raw) To: Satoshi Nagayasu <[email protected]>; +Cc: Peter Eisentraut <[email protected]>; pgsql-hackers Satoshi Nagayasu <[email protected]> writes: > First of all, I was looking for some regression tests for > CREATE/ALTER/DROP DATABASE commands, but I couldn't find them > in the test/regress/sql/ directory. So, I asked the question. > I guess these database tests are in pg_regress.c. Right? Yeah, we don't bother with explicit tests of CREATE/DROP DATABASE because that's inherently tested by creating/replacing the regression database(s). And these actions are expensive enough that I'm not eager to add several more of them to the test sequence without darn good reason. I'm not sure how much of ALTER DATABASE's functionality we're testing, though as you say pg_regress itself does some of that. It might be reasonable to add some more tests of ALTER cases. regards, tom lane -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
* [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source @ 2025-07-27 15:48 Lukas Fittl <[email protected]> 0 siblings, 0 replies; 279+ messages in thread From: Lukas Fittl @ 2025-07-27 15:48 UTC (permalink / raw) In passing also reduce the per-loop overhead caused by repeated divisions in INSTR_TIME_GET_NANOSEC when the ticks variable has become very large, instead diff first and then turn it into nanosecs. --- src/bin/pg_test_timing/pg_test_timing.c | 59 +++++++++++++++++++------ src/include/portability/instr_time.h | 30 ++++++++----- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index a5621251afc..b77ef2063b6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -16,6 +16,7 @@ static const char *progname; static unsigned int test_duration = 3; static double max_rprct = 99.99; +static bool fast_timing = false; /* record duration in powers of 2 nanoseconds */ static long long int histogram[32]; @@ -56,6 +57,7 @@ handle_args(int argc, char *argv[]) static struct option long_options[] = { {"duration", required_argument, NULL, 'd'}, {"cutoff", required_argument, NULL, 'c'}, + {"fast", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; @@ -68,7 +70,7 @@ handle_args(int argc, char *argv[]) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname); + printf(_("Usage: %s [-d DURATION] [-c CUTOFF] [--fast]\n"), progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) @@ -78,7 +80,7 @@ handle_args(int argc, char *argv[]) } } - while ((option = getopt_long(argc, argv, "d:c:", + while ((option = getopt_long(argc, argv, "d:c:f:", long_options, &optindex)) != -1) { switch (option) @@ -125,6 +127,10 @@ handle_args(int argc, char *argv[]) } break; + case 'f': + fast_timing = true; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -155,11 +161,31 @@ test_timing(unsigned int duration) uint64 total_time; int64 time_elapsed = 0; uint64 loop_count = 0; - uint64 prev, - cur; instr_time start_time, end_time, - temp; + prev, + cur; + char *time_source = NULL; + bool fast_timing_used = false; + + INSTR_TIME_INITIALIZE(); + +#if !defined(WIN32) && defined(__x86_64__) && defined(__linux__) + if (fast_timing && has_rdtsc) + { + time_source = "RDTSC"; + fast_timing_used = true; + } + else if (has_rdtscp) + time_source = "RDTSCP"; + else + time_source = PG_INSTR_CLOCK_NAME; +#else + time_source = PG_INSTR_CLOCK_NAME; +#endif + if (fast_timing && !fast_timing_used) + printf(_("Warning: Fast timing requested, but not available - regular timing source will be used\n")); + printf(_("Time source: %s\n"), time_source); /* * Pre-zero the statistics data structures. They're already zero by @@ -173,8 +199,11 @@ test_timing(unsigned int duration) total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0; - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_NANOSEC(start_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(start_time); + else + INSTR_TIME_SET_CURRENT(start_time); + cur = start_time; while (time_elapsed < total_time) { @@ -182,9 +211,11 @@ test_timing(unsigned int duration) bits; prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_NANOSEC(temp); - diff = cur - prev; + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(cur); + else + INSTR_TIME_SET_CURRENT(cur); + diff = INSTR_TIME_DIFF_NANOSEC(cur, prev); /* Did time go backwards? */ if (unlikely(diff < 0)) @@ -217,11 +248,13 @@ test_timing(unsigned int duration) largest_diff_count++; loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_NANOSEC(temp); + time_elapsed = INSTR_TIME_DIFF_NANOSEC(cur, start_time); } - INSTR_TIME_SET_CURRENT(end_time); + if (fast_timing) + INSTR_TIME_SET_CURRENT_FAST(end_time); + else + INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index e2e339a0c4f..f02296f1026 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -112,10 +112,13 @@ extern int64 max_ticks_no_overflow; */ #if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) #define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC_RAW)" #elif defined(CLOCK_MONOTONIC) #define PG_INSTR_CLOCK CLOCK_MONOTONIC +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_MONOTONIC)" #else #define PG_INSTR_CLOCK CLOCK_REALTIME +#define PG_INSTR_CLOCK_NAME "clock_gettime (CLOCK_REALTIME)" #endif #if defined(__x86_64__) && defined(__linux__) @@ -174,7 +177,7 @@ pg_get_ticks(void) } static inline int64_t -pg_ticks_to_ns(instr_time t) +pg_ticks_to_ns(int64 ticks) { /* * Would multiplication overflow? If so perform computation in two parts. @@ -183,7 +186,7 @@ pg_ticks_to_ns(instr_time t) */ int64 ns = 0; - if (unlikely(t.ticks > max_ticks_no_overflow)) + if (unlikely(ticks > max_ticks_no_overflow)) { /* * Compute how often the maximum number of ticks fits completely into @@ -192,7 +195,7 @@ pg_ticks_to_ns(instr_time t) * value. In a 2nd step we adjust the number of elapsed ticks and * convert the remaining ticks. */ - int64 count = t.ticks / max_ticks_no_overflow; + int64 count = ticks / max_ticks_no_overflow; int64 max_ns = max_ticks_no_overflow * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; ns = max_ns * count; @@ -201,11 +204,11 @@ pg_ticks_to_ns(instr_time t) * Subtract the ticks that we now already accounted for, so that they * don't get counted twice. */ - t.ticks -= count * max_ticks_no_overflow; - Assert(t.ticks >= 0); + ticks -= count * max_ticks_no_overflow; + Assert(ticks >= 0); } - ns += t.ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; + ns += ticks * ticks_per_ns_scaled / TICKS_TO_NS_PRECISION; return ns; } @@ -226,14 +229,14 @@ pg_initialize_get_ticks() #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_get_ticks()) -#define INSTR_TIME_GET_NANOSEC(t) \ - pg_ticks_to_ns(t) - +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + (pg_ticks_to_ns(ticks)) #else /* WIN32 */ /* Use QueryPerformanceCounter() */ +#define PG_INSTR_CLOCK_NAME "QueryPerformanceCounter" /* helper for INSTR_TIME_SET_CURRENT / INSTR_TIME_SET_CURRENT_FAST */ static inline instr_time @@ -265,8 +268,8 @@ GetTimerFrequency(void) #define INSTR_TIME_SET_CURRENT(t) \ ((t) = pg_query_performance_counter()) -#define INSTR_TIME_GET_NANOSEC(t) \ - ((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency()))) +#define INSTR_TIME_TICKS_TO_NANOSEC(ticks) \ + ((int64) ((ticks) * ((double) NS_PER_S / GetTimerFrequency()))) #endif /* WIN32 */ @@ -285,9 +288,14 @@ GetTimerFrequency(void) #define INSTR_TIME_SUBTRACT(x,y) \ ((x).ticks -= (y).ticks) +#define INSTR_TIME_DIFF_NANOSEC(x,y) \ + (INSTR_TIME_TICKS_TO_NANOSEC((x).ticks - (y).ticks)) + #define INSTR_TIME_ACCUM_DIFF(x,y,z) \ ((x).ticks += (y).ticks - (z).ticks) +#define INSTR_TIME_GET_NANOSEC(t) \ + (INSTR_TIME_TICKS_TO_NANOSEC((t).ticks)) #define INSTR_TIME_GET_DOUBLE(t) \ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) -- 2.47.3 --vtqqtrpooseurzip-- ^ permalink raw reply [nested|flat] 279+ messages in thread
end of thread, other threads:[~2025-07-27 15:48 UTC | newest] Thread overview: 279+ messages (download: mbox mbox.gz follow: Atom feed) -- links below jump to the message on this page -- 2013-01-07 12:14 psql \l to accept patterns Peter Eisentraut <[email protected]> 2013-01-07 19:19 ` Fabrízio de Royes Mello <[email protected]> 2013-01-07 19:23 ` Alvaro Herrera <[email protected]> 2013-01-07 20:53 ` Robert Haas <[email protected]> 2013-01-07 21:16 ` Tom Lane <[email protected]> 2013-01-07 22:14 ` Peter Eisentraut <[email protected]> 2013-01-07 22:37 ` Robert Haas <[email protected]> 2013-01-09 04:36 ` Peter Eisentraut <[email protected]> 2013-01-11 16:28 ` Robert Haas <[email protected]> 2013-01-11 16:35 ` Stephen Frost <[email protected]> 2013-01-29 05:48 ` Peter Eisentraut <[email protected]> 2013-01-29 15:20 ` Satoshi Nagayasu <[email protected]> 2013-01-29 15:34 ` Tom Lane <[email protected]> 2013-01-30 03:33 ` Satoshi Nagayasu <[email protected]> 2013-01-30 03:44 ` Tom Lane <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-27 15:48 [PATCH v12 3/3] pg_test_timing: Add --fast flag to test fast timing, report time source Lukas Fittl <[email protected]> 2025-07-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