Skip to content

time.perf_counter() should state more clearly (or deny) that it is monotonic #115637

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
anarcat opened this issue Feb 18, 2024 · 7 comments
Open
Labels
docs Documentation in the Doc dir

Comments

@anarcat
Copy link

anarcat commented Feb 18, 2024

Documentation

I was trying to figure out the distinction between perf_counter and monotonic, and it's not entirely clear to me, even after reading PEP 418. I think it is monotonic, but I'm not sure.

So I'm invoking the Python gods here: what's the actual wanted behavior here? And why isn't it more clearly stated in the docs?

Thanks!

Linked PRs

@anarcat anarcat added the docs Documentation in the Doc dir label Feb 18, 2024
@AlexWaygood
Copy link
Member

@vstinner, I think you might be the expert here? :)

@anarcat
Copy link
Author

anarcat commented Feb 18, 2024

alright, i guess #115638 should fix the docs then. :)

@gaogaotiantian
Copy link
Member

From my personal opinion, the fact that it is currently monotonic, does not mean that we should document it and make a promise about it. We already promised one thing about perf_counter() - it'll be the most precise timer on the system. If we make another promise that this'll be monotonic, then what happens if a system comes up with the most precise timer not being monotonic? We can't keep both promises anymore. That's probably just a imaginary scenario and one may argue that all the platforms we support at this time have the most precise timers as monotonic, which might be the real case at least for now.

@vstinner
Copy link
Member

I'm not 100% sure that perf_counter() is always monotonic in all cases on all platforms. It would help to have some evidence on Linux, Windows, macOS.

@vstinner
Copy link
Member

See also #88494


On Windows, QueryPerformanceCounter() is implemented by reading x86 TSC. Ten years ago, TSC causes issues when the CPU frequency changed: recent CPU now have a TSC which is no longer related to the CPU frequency. There were also a few issues related to virtualization.

Today, Windows documentation just says: https://learn.microsoft.com/en-us/windows/win32/sysinfo/acquiring-high-resolution-time-stamps

Is the performance counter monotonic (non-decreasing)?
Yes. QPC does not go backward.

where QPC stands for QueryPerformanceCounter().


Linux supports multiple clock sources and can switch to the next one at runtime if Linux decides that a clock is not reliable. Example on my laptop:

$ cat /sys/devices/system/clocksource/clocksource0/current_clocksource 
tsc
$ cat /sys/devices/system/clocksource/clocksource0/available_clocksource 
tsc acpi_pm 

TSC and ACPI PM are available and TSC is the current clock source.

@vstinner
Copy link
Member

PyTime_PerfCounter() has these implementations:

  • Windows: QueryPerformanceCounter() -- monotonic
  • macOS: mach_absolute_time() -- monotonic
  • HP-UX: gethrtime() -- monotonic
  • clock_gettime(CLOCK_HIGHRES) -- monotonic
  • clock_gettime(CLOCK_MONOTONIC) -- monotonic

Oh, in fact, Python time.get_clock_info('perf_counter') announces monotonic=True on all platforms! Example on Linux:

$ python3.13
Python 3.13.0a3 (main, Jan 18 2024, 00:00:00) [GCC 13.2.1 20231205 (Red Hat 13.2.1-6)] on linux
>>> import time
>>> time.get_clock_info('perf_counter')
namespace(implementation='clock_gettime(CLOCK_MONOTONIC)', monotonic=True, adjustable=False, resolution=1e-09)

@anarcat
Copy link
Author

anarcat commented Feb 19, 2024

from what i can tell in the Fine Source Code:

cpython/Python/pytime.c

Lines 1327 to 1335 in aa8c1a0

int
_PyTime_GetPerfCounterWithInfo(_PyTime_t *t, _Py_clock_info_t *info)
{
#ifdef MS_WINDOWS
return py_get_win_perf_counter(t, info, 1);
#else
return _PyTime_GetMonotonicClockWithInfo(t, info);
#endif
}

it does look like on everything but MS_WINDOWS, it defers to _PyTime_GetMonotonicClockWithInfo, so it's not only monotonic, it's exactly like time.monotonic().

But those are implementation details: i think the fundamental question here is whether or not the plan was to have the performance counter be monotonic in the first place, or, if that's the requirement anyway.

Looking at it from a library consumer perspective, i'm looking at the codetiming package and it's implemented with perf_counter. it's exactly the kind of module I would expect perf_counter to be used for, but I would also be extremely surprised if the counter would go backwards on time shifts, for example.

So I would personnally argue that perf_counter() is a superset of monotonic(). But I would understand if it would make things more complicated for implementers and if it wasn't the original intention of PEP418. But then I really wonder how one would be supposed to implement codetiming then: should it avoid using perf_counter() because it could eventually go backwards? or should it use both perf_counter() and monotonic and fallback on the latter if the former goes backwards? iffy...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
docs Documentation in the Doc dir
Projects
None yet
Development

No branches or pull requests

4 participants