What is a typical precision of gettimeofday()?
Over in the thread discussing the addition of UUIDv7 support [0]/messages/by-id/CAAhFRxitJv=yoGnXUgeLB_O+M7J2BJAmb5jqAT9gZ3bij3uLDA@mail.gmail.com, there
is some uncertainty about what timestamp precision one can expect from
gettimeofday().
UUIDv7 uses milliseconds since Unix epoch, but can optionally use up to
12 additional bits of timestamp precision (see [1]https://datatracker.ietf.org/doc/html/draft-ietf-uuidrev-rfc4122bis#section-6.2-5.6.1), but it can also
just use a counter instead of the extra precision. The current patch
uses the counter method "because of portability concerns" (source code
comment).
I feel that we don't actually have any information about this
portability concern. Does anyone know what precision we can expect from
gettimeofday()? Can we expect the full microsecond precision usually?
[0]: /messages/by-id/CAAhFRxitJv=yoGnXUgeLB_O+M7J2BJAmb5jqAT9gZ3bij3uLDA@mail.gmail.com
/messages/by-id/CAAhFRxitJv=yoGnXUgeLB_O+M7J2BJAmb5jqAT9gZ3bij3uLDA@mail.gmail.com
[1]: https://datatracker.ietf.org/doc/html/draft-ietf-uuidrev-rfc4122bis#section-6.2-5.6.1
https://datatracker.ietf.org/doc/html/draft-ietf-uuidrev-rfc4122bis#section-6.2-5.6.1
Hi,
cc: Andrey
Over in the thread discussing the addition of UUIDv7 support [0], there
is some uncertainty about what timestamp precision one can expect from
gettimeofday().UUIDv7 uses milliseconds since Unix epoch, but can optionally use up to
12 additional bits of timestamp precision (see [1]), but it can also
just use a counter instead of the extra precision. The current patch
uses the counter method "because of portability concerns" (source code
comment).I feel that we don't actually have any information about this
portability concern. Does anyone know what precision we can expect from
gettimeofday()? Can we expect the full microsecond precision usually?
Specifically in the UUIDv7 application the goal is to generate not
necessarily time-precise UUIDs but rather do our best to get *unique*
UUIDs. As I understand, this is the actual reason why the patch needs
counters.
As Linux man page puts it:
"""
The time returned by gettimeofday() is affected by discontinuous jumps
in the system time (e.g., if the system administrator manually
changes the system time).
"""
On top of that MacOS man page says:
"""
The resolution of the system clock is hardware dependent, and the time
may be updated continuously or in ``ticks.''
"""
On Windows our gettimeofday() implementation is a wrapper for
GetSystemTimePreciseAsFileTime(). The corresponding MSDN page [1]https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemtimepreciseasfiletime is
somewhat laconic.
Considering the number of environments PostgreSQL can run in (OS +
hardware + virtualization technologies) and the fact that
hardware/software changes I doubt that it's realistic to expect any
particular guarantees from gettimeofday() in the general case.
--
Best regards,
Aleksander Alekseev
On 19.03.24 10:38, Aleksander Alekseev wrote:
Considering the number of environments PostgreSQL can run in (OS +
hardware + virtualization technologies) and the fact that
hardware/software changes I doubt that it's realistic to expect any
particular guarantees from gettimeofday() in the general case.
If we want to be robust without any guarantees from gettimeofday(), then
arguably gettimeofday() is not the right underlying function to use for
UUIDv7. I'm not arguing that, I think we can assume some reasonable
baseline for what gettimeofday() produces. But it would be good to get
some information about what that might be.
Btw., here is util-linux saying
/* Assume that the gettimeofday() has microsecond granularity */
https://github.com/util-linux/util-linux/blob/master/libuuid/src/gen_uuid.c#L232
On Wed, 20 Mar 2024 at 07:35, Peter Eisentraut <peter@eisentraut.org> wrote:
If we want to be robust without any guarantees from gettimeofday(), then
arguably gettimeofday() is not the right underlying function to use for
UUIDv7.
There's also clock_gettime which exposes its resolution using clock_getres
On 19 Mar 2024, at 13:28, Peter Eisentraut <peter@eisentraut.org> wrote:
I feel that we don't actually have any information about this portability concern. Does anyone know what precision we can expect from gettimeofday()? Can we expect the full microsecond precision usually?
At PGConf.dev Hannu Krossing draw attention to pg_test_timing module. I’ve tried this module(slightly modified to measure nanoseconds) on some systems, and everywhere I found ~100ns resolution (95% of ticks fall into 64ns and 128ns buckets).
I’ll add cc Hannu, and also pg_test_timing module authors Ants ang Greg. Maybe they can add some context.
Best regards, Andrey Borodin.
I plan to send patch to pg_test_timing in a day or two
the underlying time precision on modern linux seems to be
2 ns for some Intel CPUs
10 ns for Zen4
40 ns for ARM (Ampere)
---
Hannu
|
On Tue, Jun 18, 2024 at 7:48 AM Andrey M. Borodin <x4mmm@yandex-team.ru>
wrote:
Show quoted text
On 19 Mar 2024, at 13:28, Peter Eisentraut <peter@eisentraut.org> wrote:
I feel that we don't actually have any information about this
portability concern. Does anyone know what precision we can expect from
gettimeofday()? Can we expect the full microsecond precision usually?At PGConf.dev Hannu Krossing draw attention to pg_test_timing module. I’ve
tried this module(slightly modified to measure nanoseconds) on some
systems, and everywhere I found ~100ns resolution (95% of ticks fall into
64ns and 128ns buckets).I’ll add cc Hannu, and also pg_test_timing module authors Ants ang Greg.
Maybe they can add some context.Best regards, Andrey Borodin.
Here is the output of nanosecond-precision pg_test_timing on M1 Macbook Air
/work/postgres/src/bin/pg_test_timing % ./pg_test_timing
Testing timing overhead for 3 seconds.
Per loop time including overhead: 21.54 ns
Histogram of timing durations:
<= ns % of total running % count
0 49.1655 49.1655 68481688
1 0.0000 49.1655 0
3 0.0000 49.1655 0
7 0.0000 49.1655 0
15 0.0000 49.1655 0
31 0.0000 49.1655 0
63 50.6890 99.8545 70603742
127 0.1432 99.9976 199411
255 0.0015 99.9991 2065
511 0.0001 99.9992 98
1023 0.0001 99.9993 140
2047 0.0002 99.9995 284
4095 0.0000 99.9995 50
8191 0.0000 99.9996 65
16383 0.0002 99.9997 240
32767 0.0001 99.9998 128
65535 0.0001 99.9999 97
131071 0.0000 99.9999 58
262143 0.0000 100.0000 44
524287 0.0000 100.0000 22
1048575 0.0000 100.0000 7
2097151 0.0000 100.0000 2
First 128 exact nanoseconds:
0 49.1655 49.1655 68481688
41 16.8964 66.0619 23534708
42 33.7926 99.8545 47069034
83 0.0835 99.9380 116362
84 0.0419 99.9799 58349
125 0.0177 99.9976 24700
As you see the 40 ns internal tick gets somehow blurred into
not-quite-40-ns timing step
On Linux / ARM Ampere where __builtin_readcyclecounter() works (it
compiles but crashes on Mac OS M1, I have not yet tested on Linux M1)
the tick is exactly 40 ns and I'd expect it to be the same on M1.
Show quoted text
On Tue, Jun 18, 2024 at 5:08 PM Hannu Krosing <hannuk@google.com> wrote:
I plan to send patch to pg_test_timing in a day or two
the underlying time precision on modern linux seems to be
2 ns for some Intel CPUs
10 ns for Zen4
40 ns for ARM (Ampere)---
Hannu|
On Tue, Jun 18, 2024 at 7:48 AM Andrey M. Borodin <x4mmm@yandex-team.ru> wrote:
On 19 Mar 2024, at 13:28, Peter Eisentraut <peter@eisentraut.org> wrote:
I feel that we don't actually have any information about this portability concern. Does anyone know what precision we can expect from gettimeofday()? Can we expect the full microsecond precision usually?
At PGConf.dev Hannu Krossing draw attention to pg_test_timing module. I’ve tried this module(slightly modified to measure nanoseconds) on some systems, and everywhere I found ~100ns resolution (95% of ticks fall into 64ns and 128ns buckets).
I’ll add cc Hannu, and also pg_test_timing module authors Ants ang Greg. Maybe they can add some context.
Best regards, Andrey Borodin.
On 18.06.24 07:47, Andrey M. Borodin wrote:
On 19 Mar 2024, at 13:28, Peter Eisentraut <peter@eisentraut.org> wrote:
I feel that we don't actually have any information about this portability concern. Does anyone know what precision we can expect from gettimeofday()? Can we expect the full microsecond precision usually?
At PGConf.dev Hannu Krossing draw attention to pg_test_timing module. I’ve tried this module(slightly modified to measure nanoseconds) on some systems, and everywhere I found ~100ns resolution (95% of ticks fall into 64ns and 128ns buckets).
AFAICT, pg_test_timing doesn't use gettimeofday(), so this doesn't
really address the original question.
Peter Eisentraut <peter@eisentraut.org> writes:
AFAICT, pg_test_timing doesn't use gettimeofday(), so this doesn't
really address the original question.
It's not exactly hard to make it do so (see attached).
I tried this on several different machines, and my conclusion is that
gettimeofday() reports full microsecond precision on any platform
anybody is likely to be running PG on today. Even my one surviving
pet dinosaur, NetBSD 10 on PowerPC Mac (mamba), shows results like
this:
$ ./pg_test_timing
Testing timing overhead for 3 seconds.
Per loop time including overhead: 901.41 ns
Histogram of timing durations:
< us % of total count
1 10.46074 348148
2 89.51495 2979181
4 0.00574 191
8 0.00430 143
16 0.00691 230
32 0.00376 125
64 0.00012 4
128 0.00303 101
256 0.00027 9
512 0.00009 3
1024 0.00009 3
I also modified pg_test_timing to measure nanoseconds not
microseconds (second patch attached), and got this:
$ ./pg_test_timing
Testing timing overhead for 3 seconds.
Per loop time including overhead: 805.50 ns
Histogram of timing durations:
< ns % of total count
1 19.84234 739008
2 0.00000 0
4 0.00000 0
8 0.00000 0
16 0.00000 0
32 0.00000 0
64 0.00000 0
128 0.00000 0
256 0.00000 0
512 0.00000 0
1024 80.14013 2984739
2048 0.00078 29
4096 0.00658 245
8192 0.00290 108
16384 0.00252 94
32768 0.00250 93
65536 0.00016 6
131072 0.00185 69
262144 0.00008 3
524288 0.00008 3
1048576 0.00008 3
confirming that when the result changes it generally does so by 1usec.
Applying just the second patch, I find that clock_gettime on this
old hardware seems to be limited to 1us resolution, but on my more
modern machines (mac M1, x86_64) it can tick at 40ns or less.
Even a raspberry pi 4 shows
$ ./pg_test_timing
Testing timing overhead for 3 seconds.
Per loop time including overhead: 69.12 ns
Histogram of timing durations:
< ns % of total count
1 0.00000 0
2 0.00000 0
4 0.00000 0
8 0.00000 0
16 0.00000 0
32 0.00000 0
64 37.59583 16317040
128 62.38568 27076131
256 0.01674 7265
512 0.00002 8
1024 0.00000 0
2048 0.00000 0
4096 0.00153 662
8192 0.00019 83
16384 0.00001 3
32768 0.00001 5
suggesting that the clock_gettime resolution is better than 64 ns.
So I concur with Hannu that it's time to adjust pg_test_timing to
resolve nanoseconds not microseconds. I gather he's created a
patch that does more than mine below, so I'll wait for that.
regards, tom lane
Attachments:
use-gettimeofday-for-instr_time.patchtext/x-diff; charset=us-ascii; name=use-gettimeofday-for-instr_time.patchDownload
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index a6fc1922f2..5509d23d2f 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -84,7 +84,7 @@ typedef struct instr_time
/* Use clock_gettime() */
-#include <time.h>
+#include <sys/time.h>
/*
* The best clockid to use according to the POSIX spec is CLOCK_MONOTONIC,
@@ -111,10 +111,10 @@ static inline instr_time
pg_clock_gettime_ns(void)
{
instr_time now;
- struct timespec tmp;
+ struct timeval tmp;
- clock_gettime(PG_INSTR_CLOCK, &tmp);
- now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
+ gettimeofday(&tmp, NULL);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_usec * 1000;
return now;
}
measure-nsec-in-pg_test_timing.patchtext/x-diff; charset=us-ascii; name=measure-nsec-in-pg_test_timing.patchDownload
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index c29d6f8762..ea2b565b14 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -133,7 +133,7 @@ test_timing(unsigned int duration)
total_time = duration > 0 ? duration * INT64CONST(1000000) : 0;
INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_MICROSEC(start_time);
+ cur = INSTR_TIME_GET_NANOSEC(start_time);
while (time_elapsed < total_time)
{
@@ -142,7 +142,7 @@ test_timing(unsigned int duration)
prev = cur;
INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_MICROSEC(temp);
+ cur = INSTR_TIME_GET_NANOSEC(temp);
diff = cur - prev;
/* Did time go backwards? */
@@ -183,7 +183,7 @@ output(uint64 loop_count)
{
int64 max_bit = 31,
i;
- char *header1 = _("< us");
+ char *header1 = _("< ns");
char *header2 = /* xgettext:no-c-format */ _("% of total");
char *header3 = _("count");
int len1 = strlen(header1);
(resending to list and other CC:s )
Hi Tom
This is my current patch which also adds running % and optionally uses
faster way to count leading zeros, though I did not see a change from
that.
It also bucketizes first 128 ns to get better overview of exact behaviour.
We may want to put reporting this behind a flag
---
Hannu
Show quoted text
On Wed, Jun 19, 2024 at 6:36 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
Peter Eisentraut <peter@eisentraut.org> writes:
AFAICT, pg_test_timing doesn't use gettimeofday(), so this doesn't
really address the original question.It's not exactly hard to make it do so (see attached).
I tried this on several different machines, and my conclusion is that
gettimeofday() reports full microsecond precision on any platform
anybody is likely to be running PG on today. Even my one surviving
pet dinosaur, NetBSD 10 on PowerPC Mac (mamba), shows results like
this:$ ./pg_test_timing
Testing timing overhead for 3 seconds.
Per loop time including overhead: 901.41 ns
Histogram of timing durations:
< us % of total count
1 10.46074 348148
2 89.51495 2979181
4 0.00574 191
8 0.00430 143
16 0.00691 230
32 0.00376 125
64 0.00012 4
128 0.00303 101
256 0.00027 9
512 0.00009 3
1024 0.00009 3I also modified pg_test_timing to measure nanoseconds not
microseconds (second patch attached), and got this:$ ./pg_test_timing
Testing timing overhead for 3 seconds.
Per loop time including overhead: 805.50 ns
Histogram of timing durations:
< ns % of total count
1 19.84234 739008
2 0.00000 0
4 0.00000 0
8 0.00000 0
16 0.00000 0
32 0.00000 0
64 0.00000 0
128 0.00000 0
256 0.00000 0
512 0.00000 0
1024 80.14013 2984739
2048 0.00078 29
4096 0.00658 245
8192 0.00290 108
16384 0.00252 94
32768 0.00250 93
65536 0.00016 6
131072 0.00185 69
262144 0.00008 3
524288 0.00008 3
1048576 0.00008 3confirming that when the result changes it generally does so by 1usec.
Applying just the second patch, I find that clock_gettime on this
old hardware seems to be limited to 1us resolution, but on my more
modern machines (mac M1, x86_64) it can tick at 40ns or less.
Even a raspberry pi 4 shows$ ./pg_test_timing
Testing timing overhead for 3 seconds.
Per loop time including overhead: 69.12 ns
Histogram of timing durations:
< ns % of total count
1 0.00000 0
2 0.00000 0
4 0.00000 0
8 0.00000 0
16 0.00000 0
32 0.00000 0
64 37.59583 16317040
128 62.38568 27076131
256 0.01674 7265
512 0.00002 8
1024 0.00000 0
2048 0.00000 0
4096 0.00153 662
8192 0.00019 83
16384 0.00001 3
32768 0.00001 5suggesting that the clock_gettime resolution is better than 64 ns.
So I concur with Hannu that it's time to adjust pg_test_timing to
resolve nanoseconds not microseconds. I gather he's created a
patch that does more than mine below, so I'll wait for that.regards, tom lane
Attachments:
pg_test_timing.nanoseconds.patchtext/x-patch; charset=US-ASCII; name=pg_test_timing.nanoseconds.patchDownload
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index c29d6f8762..20b2785f50 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -19,9 +19,12 @@ static void handle_args(int argc, char *argv[]);
static uint64 test_timing(unsigned int duration);
static void output(uint64 loop_count);
-/* record duration in powers of 2 microseconds */
+/* record duration in powers of 2 nanoseconds */
long long int histogram[32];
+/* record duration of first 128 ns directly */
+long long int direct_histogram[128];
+
int
main(int argc, char *argv[])
{
@@ -130,10 +133,10 @@ test_timing(unsigned int duration)
end_time,
temp;
- total_time = duration > 0 ? duration * INT64CONST(1000000) : 0;
+ total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
INSTR_TIME_SET_CURRENT(start_time);
- cur = INSTR_TIME_GET_MICROSEC(start_time);
+ cur = INSTR_TIME_GET_NANOSEC(start_time);
while (time_elapsed < total_time)
{
@@ -142,7 +145,7 @@ test_timing(unsigned int duration)
prev = cur;
INSTR_TIME_SET_CURRENT(temp);
- cur = INSTR_TIME_GET_MICROSEC(temp);
+ cur = INSTR_TIME_GET_NANOSEC(temp);
diff = cur - prev;
/* Did time go backwards? */
@@ -153,19 +156,25 @@ test_timing(unsigned int duration)
exit(1);
}
+ if(likely(diff < 128))
+ direct_histogram[diff]++;
+
+#if defined(__has_builtin) && __has_builtin(__builtin_clz)
+ bits = 32 - __builtin_clz(diff);
+#else
/* What is the highest bit in the time diff? */
while (diff)
{
diff >>= 1;
bits++;
}
-
+#endif
/* Update appropriate duration bucket */
histogram[bits]++;
loop_count++;
INSTR_TIME_SUBTRACT(temp, start_time);
- time_elapsed = INSTR_TIME_GET_MICROSEC(temp);
+ time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
}
INSTR_TIME_SET_CURRENT(end_time);
@@ -183,26 +192,46 @@ output(uint64 loop_count)
{
int64 max_bit = 31,
i;
- char *header1 = _("< us");
+ char *header1 = _("<= ns");
char *header2 = /* xgettext:no-c-format */ _("% of total");
+ char *header2a = /* xgettext:no-c-format */ _("running %");
char *header3 = _("count");
int len1 = strlen(header1);
int len2 = strlen(header2);
+ int len2a = strlen(header2a);
int len3 = strlen(header3);
+ float rprct;
/* find highest bit value */
while (max_bit > 0 && histogram[max_bit] == 0)
max_bit--;
printf(_("Histogram of timing durations:\n"));
- printf("%*s %*s %*s\n",
- Max(6, len1), header1,
+ printf("%*s %*s %*s %*s\n",
+ Max(8, len1), header1,
Max(10, len2), header2,
+ Max(10, len2a), header2a,
Max(10, len3), header3);
- for (i = 0; i <= max_bit; i++)
- printf("%*ld %*.5f %*lld\n",
- Max(6, len1), 1l << i,
- Max(10, len2) - 1, (double) histogram[i] * 100 / loop_count,
- Max(10, len3), histogram[i]);
+ for (i = 0, rprct=0; i <= max_bit; i++){
+ rprct += (double) histogram[i] * 100 / loop_count;
+ printf("%*ld %*.4f %*.4f %*lld\n",
+ Max(8, len1), (1l << i) - 1,
+ Max(10, len2) - 1, (double) histogram[i] * 100 / loop_count,
+ Max(10, len2a) - 1, rprct,
+ Max(10, len3), histogram[i]);
+ }
+
+ printf("First 128 nanoseconds:\n");
+ for (i = 0, rprct=0; i < 128; i++){
+ rprct += (double) direct_histogram[i] * 100 / loop_count;
+ if (direct_histogram[i])
+ printf("%*ld %*.4f %*.4f %*lld\n",
+ Max(8, len1), i,
+ Max(10, len2) - 1, (double) direct_histogram[i] * 100 / loop_count,
+ Max(10, len2a) - 1, rprct,
+ Max(10, len3), direct_histogram[i]);
+ }
+
}
+
I also have a variant that uses the low-level CPU cycle counter
directly (attached)
It currently only works on clang, as it is done using
__builtin_readcyclecounter() in order to support both x64 and ARM.
This one is there to understand the overhead of the calculations when
going from cycle counter to POSIX time struct
This works OK with Clang, but we should probably not integrate this
directly into the code as it has some interesting corner cases. For
example Apple's clang does compile __builtin_readcyclecounter() but
crashes with unknown instruction when trying to run it.
Therefore I have not integrated it into Makefile so if you want to use
it, just copy it into src/bin/pg_test_timing and run
cd src/bin/pgtest_timing
mv pg_test_timing.c pg_test_timing.c.backup
cp pg_test_cyclecounter.c pg_test_timing.c
make
mv pg_test_timing pg_test_cyclecounter
mv pg_test_timing.c.backup pg_test_timing.c
It gives output like
Testing timing overhead for 3 seconds.
Total 25000001 ticks in 1000000073 ns, 24999999.175000 ticks per ns
This CPU is running at 24999999 ticks / second, will run test for 74999997 ticks
loop_count 287130958Per loop time including overhead: 10.45 ns, min: 0
ticks (0.0 ns), same: 212854591
Total ticks in: 74999997, in: 3000000541 nr
Log2 histogram of timing durations:
< ticks ( < ns) % of total running % count
1 ( 40.0) 74.1315 74.1315 212854591
2 ( 80.0) 25.8655 99.9970 74267876
4 ( 160.0) 0.0000 99.9970 7
8 ( 320.0) 0.0000 99.9970 3
16 ( 640.0) 0.0000 99.9970 1
32 ( 1280.0) 0.0000 99.9971 27
64 ( 2560.0) 0.0012 99.9983 3439
128 ( 5120.0) 0.0016 99.9999 4683
256 ( 10240.0) 0.0001 100.0000 265
512 ( 20480.0) 0.0000 100.0000 37
1024 ( 40960.0) 0.0000 100.0000 23
2048 ( 81920.0) 0.0000 100.0000 6
First 64 ticks --
0 ( 0.0) 74.1315 74.1315 212854591
1 ( 40.0) 25.8655 99.9970 74267876
2 ( 80.0) 0.0000 99.9970 2
3 ( 120.0) 0.0000 99.9970 5
4 ( 160.0) 0.0000 99.9970 2
6 ( 240.0) 0.0000 99.9983 1
13 ( 520.0) 0.0000 100.0000 1
...
59 ( 2360.0) 0.0000 100.0000 140
60 ( 2400.0) 0.0001 100.0000 210
61 ( 2440.0) 0.0002 100.0000 497
62 ( 2480.0) 0.0002 100.0000 524
63 ( 2520.0) 0.0001 100.0000 391
If you run on some interesting hardware, please share the results.
If we her enough I will put them together in a spreadsheet and share
I also attach my lightning talk slides here
---
Hannu
Show quoted text
On Thu, Jun 20, 2024 at 12:41 PM Hannu Krosing <hannuk@google.com> wrote:
(resending to list and other CC:s )
Hi Tom
This is my current patch which also adds running % and optionally uses
faster way to count leading zeros, though I did not see a change from
that.It also bucketizes first 128 ns to get better overview of exact behaviour.
We may want to put reporting this behind a flag
---
HannuOn Wed, Jun 19, 2024 at 6:36 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
Peter Eisentraut <peter@eisentraut.org> writes:
AFAICT, pg_test_timing doesn't use gettimeofday(), so this doesn't
really address the original question.It's not exactly hard to make it do so (see attached).
I tried this on several different machines, and my conclusion is that
gettimeofday() reports full microsecond precision on any platform
anybody is likely to be running PG on today. Even my one surviving
pet dinosaur, NetBSD 10 on PowerPC Mac (mamba), shows results like
this:$ ./pg_test_timing
Testing timing overhead for 3 seconds.
Per loop time including overhead: 901.41 ns
Histogram of timing durations:
< us % of total count
1 10.46074 348148
2 89.51495 2979181
4 0.00574 191
8 0.00430 143
16 0.00691 230
32 0.00376 125
64 0.00012 4
128 0.00303 101
256 0.00027 9
512 0.00009 3
1024 0.00009 3I also modified pg_test_timing to measure nanoseconds not
microseconds (second patch attached), and got this:$ ./pg_test_timing
Testing timing overhead for 3 seconds.
Per loop time including overhead: 805.50 ns
Histogram of timing durations:
< ns % of total count
1 19.84234 739008
2 0.00000 0
4 0.00000 0
8 0.00000 0
16 0.00000 0
32 0.00000 0
64 0.00000 0
128 0.00000 0
256 0.00000 0
512 0.00000 0
1024 80.14013 2984739
2048 0.00078 29
4096 0.00658 245
8192 0.00290 108
16384 0.00252 94
32768 0.00250 93
65536 0.00016 6
131072 0.00185 69
262144 0.00008 3
524288 0.00008 3
1048576 0.00008 3confirming that when the result changes it generally does so by 1usec.
Applying just the second patch, I find that clock_gettime on this
old hardware seems to be limited to 1us resolution, but on my more
modern machines (mac M1, x86_64) it can tick at 40ns or less.
Even a raspberry pi 4 shows$ ./pg_test_timing
Testing timing overhead for 3 seconds.
Per loop time including overhead: 69.12 ns
Histogram of timing durations:
< ns % of total count
1 0.00000 0
2 0.00000 0
4 0.00000 0
8 0.00000 0
16 0.00000 0
32 0.00000 0
64 37.59583 16317040
128 62.38568 27076131
256 0.01674 7265
512 0.00002 8
1024 0.00000 0
2048 0.00000 0
4096 0.00153 662
8192 0.00019 83
16384 0.00001 3
32768 0.00001 5suggesting that the clock_gettime resolution is better than 64 ns.
So I concur with Hannu that it's time to adjust pg_test_timing to
resolve nanoseconds not microseconds. I gather he's created a
patch that does more than mine below, so I'll wait for that.regards, tom lane
Attachments:
how fast is timing.pdfapplication/pdf; name="how fast is timing.pdf"Download
%PDF-1.4
% ����
3
0
obj
<<
/Type
/Catalog
/Names
<<
>>
/PageLabels
<<
/Nums
[
0
<<
/S
/D
/St
1
>>
]
>>
/Outlines
2
0
R
/Pages
1
0
R
>>
endobj
4
0
obj
<<
/Creator
(�� G o o g l e)
/Title
(�� h o w f a s t i s t i m i n g)
>>
endobj
5
0
obj
<<
/Type
/Page
/Parent
1
0
R
/MediaBox
[
0
0
720
405
]
/Contents
6
0
R
/Resources
7
0
R
/Annots
9
0
R
/Group
<<
/S
/Transparency
/CS
/DeviceRGB
>>
>>
endobj
6
0
obj
<<
/Filter
/FlateDecode
/Length
8
0
R
>>
stream
x��S�NA��[���������A�H���� 1
���z�YX�8���z�]��^���y�hn���p��q.����l�zL������Y�mO�@r�ttR\�{�#���������3�����^��8>�>�E-�������Bj��Rep^���($S%�V��{�a��XRFR���V^��"�VI������L8n^Tx��M��l���$�@��XL��z g��6>0o,�JrY�W<�3�4�Z0�y]�
������� &ox��A��g�ka���wtf�R����M]���0m8�[)�����"�
f� ��J�x�/�f���(75T�O��������-�����Rt�+G�!����:���{��h��+�����,�����?k�7���N
endstream
endobj
8
0
obj
392
endobj
9
0
obj
[
]
endobj
10
0
obj
<<
/CA
1.0
/ca
1.0
>>
endobj
11
0
obj
<<
/CA
0
/ca
0
>>
endobj
7
0
obj
<<
/Font
<<
/Font2
12
0
R
>>
/Pattern
<<
>>
/XObject
<<
>>
/ExtGState
<<
/Alpha0
10
0
R
/Alpha1
11
0
R
>>
/ProcSet
[
/PDF
/Text
/ImageB
/ImageC
/ImageI
]
>>
endobj
13
0
obj
<<
/Type
/Page
/Parent
1
0
R
/MediaBox
[
0
0
720
405
]
/Contents
14
0
R
/Resources
15
0
R
/Annots
17
0
R
/Group
<<
/S
/Transparency
/CS
/DeviceRGB
>>
>>
endobj
14
0
obj
<<
/Filter
/FlateDecode
/Length
16
0
R
>>
stream
x��S�N1���~�H����T��@���+�VhZ(���I�NZ�@Q��S�����AI��� ��x3�9�DT���ol��R
S0�z���������qH� a�$s��O���%�R���W�'p�^���J����J���LLLa�����"�3T�:dE��L(RvY��R4ZzM��'L��Z���
��z���W
�H���K��������Y�����0�\��P��k���B\�0�-�r������9��D\�B� G��&�`%�����)=���;f����*Pe�
�[��V���9�R��'IQ3�r����TAy�]���
�~���~���L���1,2�.=��*��w6��>~-��:d��|�m�Nj;h��m(�V�����:z�X�M!��t�N��� ���O
endstream
endobj
16
0
obj
411
endobj
17
0
obj
[
]
endobj
18
0
obj
<<
/Subtype
/Image
/Interpolate
true
/Width
639
/Height
580
/ColorSpace
/DeviceRGB
/BitsPerComponent
8
/Filter
/DCTDecode
/Length
20
0
R
>>
stream
���� JFIF �� C
%# , #&')*)-0-(0%()(�� C
(((((((((((((((((((((((((((((((((((((((((((((((((((�� D"