Skip to content

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

Closed

Description

@anarcat

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

Activity

  1. AlexWaygood commented on Feb 18, 2024

    @AlexWaygood
    Member

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

  2. anarcat commented on Feb 18, 2024

    @anarcat
    Author

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

  3. gaogaotiantian commented on Feb 18, 2024

    @gaogaotiantian
    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.

  4. vstinner commented on Feb 19, 2024

    @vstinner
    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.

  5. vstinner commented on Feb 19, 2024

    @vstinner
    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.

  6. vstinner commented on Feb 19, 2024

    @vstinner
    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)
    
  7. anarcat commented on Feb 19, 2024

    @anarcat
    Author

    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...

  8. StanFromIreland commented on Jun 5, 2026

    @StanFromIreland
    Member

    Closing, this was documented in 846ad5a.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    docsDocumentation in the Doc dir

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions