Re: Reduce timing overhead of EXPLAIN ANALYZE using rdtsc?

Lukas Fittl <lukas@fittl.com>

From: Lukas Fittl <lukas@fittl.com>
To: Zsolt Parragi <zsolt.parragi@percona.com>
Cc: Andres Freund <andres@anarazel.de>, John Naylor <johncnaylorls@gmail.com>, Jakub Wartak <jakub.wartak@enterprisedb.com>, Hannu Krosing <hannuk@google.com>, Robert Haas <robertmhaas@gmail.com>, Pavel Stehule <pavel.stehule@gmail.com>, vignesh C <vignesh21@gmail.com>, Michael Paquier <michael@paquier.xyz>, Ibrar Ahmed <ibrar.ahmad@gmail.com>, Maciek Sakrejda <m.sakrejda@gmail.com>, pgsql-hackers <pgsql-hackers@postgresql.org>, David Geier <geidav.pg@gmail.com>
Date: 2026-03-11T21:02:11Z
Lists: pgsql-hackers
Hi Zsolt,

Thanks for reviewing!

On Wed, Mar 11, 2026 at 12:52 PM Zsolt Parragi
<zsolt.parragi@percona.com> wrote:
>
> + /*
> + * Once freq_khz / prev_freq_khz is small, check if it stays that way.
> + * If it does for long enough, we've got a winner frequency.
> + */
> + if (prev_freq_khz != 0 && fabs(freq_khz / prev_freq_khz) < 1.0001)
>
> Doesn't this accept any frequency decrease as stable? Shouldn't it
> instead use something like
>
> fabs(1 - freq_khz / prev_freq_khz) < 0.0001
>
> which works in both directions?

Yeah, good point - I think you're correct.

I'll wait for Andres take another look at the TSC calibration logic as
written in the patch to see if there are further suggestions on how to
adjust, but otherwise will make that change in the next revision.

> - total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
> + INSTR_TIME_SET_NANOSEC(duration_time, duration > 0 ? duration * NS_PER_S : 0);
>
>   INSTR_TIME_SET_CURRENT(start_time);
> - cur = INSTR_TIME_GET_NANOSEC(start_time);
> + cur = start_time;
>
> - while (time_elapsed < total_time)
> + end_time = start_time;
> + INSTR_TIME_ADD(end_time, duration_time);
>
> Is this correct? Won't INSTR_TIME_ADD add nanoseconds (duration_time)
> to TSC ticks (end_time)? INSTR_TIME_GET_NANOSEC uses pg_ticks_to_ns,
> but INSTR_TIME_SET_NANOSEC simply stores the value without any
> conversion function.

Ah, good catch - we basically have to do the inverse of pg_ticks_to_ns
when PG_INSTR_TICKS_TO_NS is active and ticks_per_ns_scaled is
non-zero.

I think having INSTR_TIME_SET_NANOSEC around to be able to set a
"target time" that then gets compared against in a loop seems more
generally useful, e.g. it could allow using the instr_time
infrastructure for cases like the one reported at [0], so I'll go add
that in the next revision, unless I hear feedback the other way.

> Also, if the clock source is switched during statement we get totally
> incorrect results. I don't think this has any real consequences in the
> current use case, but maybe it's worth at least mentioning somewhere?

Yeah, that's basically by design, because we can't invalidate prior
instr_time values when the timing clock source changes. I don't think
this is a big problem in practice, since changing the timing source
would be a rare thing to do - I think its mainly useful to do so
interactively when confirming numbers in case the values look off with
TSC.

Maybe we should emit a warning from the assign function when called
from inside a transaction? (since that'd be a clear cut case where its
not a good idea to do the SET)

Alternatively documenting this explicitly sounds good. Were you
thinking of user-facing documentation, or more on the code side?

Thanks,
Lukas

[0]: https://www.postgresql.org/message-id/flat/CAJhEC07OK8J7tLUbyiccnuOXRE7UKxBNqD2-pLfeFXa%3DtBoWtw%40mail.gmail.com

-- 
Lukas Fittl



Commits

Same data as JSON: GET /api/v1/messages/:b64id/commits the thread's linked commits as JSON, with link sources. API reference →
  1. pg_test_timing: Show additional TSC clock source debug info

  2. instrumentation: Avoid CPUID 0x15/0x16 for Hypervisor TSC frequency

  3. pg_test_timing: Also test RDTSC[P] timing, report time source, TSC frequency

  4. Allow retrieving x86 TSC frequency/flags from CPUID

  5. instrumentation: Standardize ticks to nanosecond conversion method

  6. instrumentation: Use Time-Stamp Counter on x86-64 to lower overhead

  7. Check for __cpuidex and __get_cpuid_count separately

  8. pg_test_timing: Reduce per-loop overhead

  9. Refactor handling of x86 CPUID instructions

  10. instrumentation: Drop INSTR_TIME_SET_CURRENT_LAZY macro

  11. Rename pg_crc32c_sse42_choose.c for general purpose

  12. Zero initialize uses of instr_time about to trigger compiler warnings

  13. instr_time: Represent time as an int64 on all platforms

  14. Add 250c8ee07ed to git-blame-ignore-revs